TextParaClient.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / PtsHost / TextParaClient.cs / 1 / TextParaClient.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: ContainerParagraph.cs 
//
// Description: TextParagClient is responsible for handling display related 
//              data of text paragraphs. 
//
// History: 
//  05/05/2003 : grzegorz - moving from Avalon branch.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Diagnostics; 
using System.Security;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.TextFormatting; 
using System.Windows.Documents;
using MS.Internal; 
using MS.Internal.Documents; 
using MS.Internal.Text;
 
using MS.Internal.PtsHost.UnsafeNativeMethods;

namespace MS.Internal.PtsHost
{ 
    // ---------------------------------------------------------------------
    // TextParaClient is responsible for handling display related data of 
    // text paragraphs. 
    // ---------------------------------------------------------------------
    internal sealed class TextParaClient : BaseParaClient 
    {
        // ------------------------------------------------------------------
        // Constructor.
        // 
        //      paragraph - Paragraph associated with this object.
        // ----------------------------------------------------------------- 
        internal TextParaClient(TextParagraph paragraph) : base(paragraph) 
        {
        } 

        // ------------------------------------------------------------------
        //
        //  Internal Methods 
        //
        // ------------------------------------------------------------------ 
 
        #region Internal Methods
 
        // -----------------------------------------------------------------
        // Validate visual node associated with paragraph.
        //
        //      fskupdInherited - inherited update info 
        //      fswdir - inherited flow direction
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            RenderSimpleLines and RenderCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to RenderSimpleLines and RenderCompositeLines
        ///        is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) 
        {
            // Query paragraph details and render its content 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            VisualCollection visualChildren = _visual.Children; 
            ContainerVisual lineContainerVisual = _visual;
 
            bool ignoreUpdateInfo = false; 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (visualChildren.Count != 0 && !(visualChildren[0] is LineVisual))
                { 
                    visualChildren.Clear(); 
                    ignoreUpdateInfo = true;
                } 


                if(IsDeferredVisualCreationSupported(ref textDetails.u.full))
                { 
                    // Transition to from no deferred visuals to deferred visuals -- Ignore update info
                    if(_lineIndexFirstVisual == -1 && lineContainerVisual.Children.Count > 0) 
                    { 
                        ignoreUpdateInfo = true;
                    } 

                    SyncUpdateDeferredLineVisuals(lineContainerVisual.Children, ref textDetails.u.full, ignoreUpdateInfo);
                }
                else 
                {
                    // Transition from deferred visuals to no deferred visuals -- Ignore update info 
                    if(_lineIndexFirstVisual != -1) 
                    {
                        _lineIndexFirstVisual = -1; 
                        lineContainerVisual.Children.Clear();
                    }

                    // If we have no children, update isn't really possible. 
                    if(lineContainerVisual.Children.Count == 0)
                    { 
                        ignoreUpdateInfo = true; 
                    }
 
                    // Add visuals for all lines.
                    if (textDetails.u.full.cLines > 0)
                    {
                        if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                        {
                            // (a) full with simple lines 
                            RenderSimpleLines(lineContainerVisual, ref textDetails.u.full, ignoreUpdateInfo); 
                        }
                        else 
                        {
                            // (b) full with composite lines - when figures/floaters are present
                            RenderCompositeLines(lineContainerVisual, ref textDetails.u.full, ignoreUpdateInfo);
                        } 
                    }
                    else 
                    { 
                        lineContainerVisual.Children.Clear();
                    } 
                }


                // Add visuals for floaters and figures. 
                if (textDetails.u.full.cAttachedObjects > 0)
                { 
                    ValidateVisualFloatersAndFigures(fskupdInherited, textDetails.u.full.cAttachedObjects); 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            // Mirror lines around the page.
            if(ThisFlowDirection != PageFlowDirection) 
            {
                PTS.FSRECT pageRect = _pageContext.PageRect;
                PtsHelper.UpdateMirroringTransform(PageFlowDirection, ThisFlowDirection, lineContainerVisual, TextDpi.FromTextDpi(2 * pageRect.u + pageRect.du));
            } 

        } 
 
        // -----------------------------------------------------------------
        // Updates viewport 
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, PtsHelper.FloaterListFromParagraph
        ///            PtsHelper.FigureListFromParagraph and UpdateViewportSimpleLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to UpdateViewportSimpleLines, PtsHelper.FloaterListFromParagraph, 
        ///        PtsHelper.FigureListFromParagraph and IsDeferredVisualCreationSupported is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateViewport(ref PTS.FSRECT viewport)
        {
            // Here's where the magic happens. 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected."); 

            if (IsDeferredVisualCreationSupported(ref textDetails.u.full)) 
            {
                // Query paragraph details and render its content
                ContainerVisual lineContainerVisual = _visual;
 
                Debug.Assert(!((TextParagraph) Paragraph).HasFiguresFloatersOrInlineObjects());
 
                UpdateViewportSimpleLines(lineContainerVisual, ref textDetails.u.full, ref viewport); 
            }
 
            int attachedObjectCount = textDetails.u.full.cAttachedObjects;

            // Recurse into figures and floaters
            if (attachedObjectCount > 0) 
            {
                // Get list of attached objects 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, attachedObjectCount, out arrayAttachedObjectDesc);
 
                // Arrange attached objects
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 

                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient); 

                    paraClient.UpdateViewport(ref viewport); 
                }
            }
        }
 
        // -----------------------------------------------------------------
        // Hit tests to the correct IInputElement within the paragraph 
        // that the mouse is over. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails,
        ///            PTS.FsQueryFloaterDetails, InputHitTestSimpleLines,
        ///            InputHitTestCompositeLines and some PtsHelper functions.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override IInputElement InputHitTest(PTS.FSPOINT pt) 
        {
            IInputElement ie = null;

            // Query paragraph details and hittest its content 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                PTS.FSPOINT localPoint = pt;
 
                // Mirror input point around page to hit test lines. 
                if(ThisFlowDirection != PageFlowDirection)
                { 
                    localPoint.u = _pageContext.PageRect.du - localPoint.u;
                }

                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    { 
                        // (a) full with simple lines
                        ie = InputHitTestSimpleLines(localPoint, ref textDetails.u.full); 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        ie = InputHitTestCompositeLines(localPoint, ref textDetails.u.full);
                    } 
                } 
                // Attached object hit tests are handled at page context level, as they're logically floating elements.
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            // If nothing is hit, return the owner of the paragraph.
            if (ie == null) 
            {
                ie = Paragraph.Element as IInputElement;
            }
 
            return ie;
        } 
 
        // -----------------------------------------------------------------
        // Returns ArrayList of rectangles for the given ContentElement 
        // if it is found. Returns empty list otherwise.
        // start: int representing start position for e relative to base TextContainer.
        // length: int representing number of positions occupied by e.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical, because calls Critical functions PTS.FsQueryTextDetails, 
        ///     PTS.FsQueryFloaterDetails, PTS.FsTransformRectangle, 
        ///     GetRectanglesInSimpleLines, GetRectanglesInCompositeLines and
        ///     some PtsHelper functions. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override List GetRectangles(ContentElement e, int start, int length) 
        { 
            List rectangles = new List();
            Debug.Assert(Paragraph.Element as ContentElement != e); 

            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                // Check figures and floaters
                if (textDetails.u.full.cAttachedObjects > 0)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION[] arrayAttachedObjectDesc;
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 
 
                    for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                    { 
                        PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];

                        BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                        PTS.ValidateHandle(paraClient); 

                        if (start < paraClient.Paragraph.ParagraphEndCharacterPosition) 
                        { 
                            rectangles = paraClient.GetRectangles(e, start, length);
                            Invariant.Assert(rectangles != null); 
                            if (rectangles.Count != 0)
                            {
                                break;
                            } 
                        }
                    } 
                } 

                // If no success with figures and floaters, check in line 
                if (rectangles.Count == 0 && textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        rectangles = GetRectanglesInSimpleLines(e, start, length, ref textDetails.u.full); 
                    } 
                    else
                    { 
                        // (b) full with complex lines
                        rectangles = GetRectanglesInCompositeLines(e, start, length, ref textDetails.u.full);
                    }
 
                    // Ensure these are specified in page coordinates.
                    if(rectangles.Count > 0 && ThisFlowDirection != PageFlowDirection) 
                    { 
                        PTS.FSRECT pageRect = _pageContext.PageRect;
 
                        for(int index = 0; index < rectangles.Count; index++)
                        {
                            PTS.FSRECT rectTransform = new PTS.FSRECT(rectangles[index]);
                            PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                            rectangles[index] = rectTransform.FromTextDpi();
                        } 
                    } 
                }
 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            Invariant.Assert(rectangles != null); 
            return rectangles;
        }

        // ------------------------------------------------------------------ 
        // Create paragraph result representing this paragraph.
        // ----------------------------------------------------------------- 
        internal override ParagraphResult CreateParagraphResult() 
        {
            return new TextParagraphResult(this); 
        }

        // ------------------------------------------------------------------
        // Returns a collection of LineResults for the paragraph. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails, 
        ///            LineResultsFromSimpleLines and LineResultsFromCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to LineResultsFromSimpleLines and
        ///        LineResultsFromCompositeLines is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetLineResults() 
        { 
#if TEXTPANELLAYOUTDEBUG
            TextPanelDebug.IncrementCounter("TextPara.GetLines", TextPanelDebug.Category.TextView); 
#endif
            ReadOnlyCollection lines = new ReadOnlyCollection(new List(0));

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0)
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines 
                        lines = LineResultsFromSimpleLines(ref textDetails.u.full);
                    }
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        lines = LineResultsFromCompositeLines(ref textDetails.u.full); 
                    } 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            return lines;
        } 

        // -----------------------------------------------------------------
        // Returns a collection of UIElements representing floated objects.
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails and 
        ///            PtsHelper.FloaterListFromParagraph. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetFloaters() 
        {
            List floaters = null; 
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // Floaters are only supported by full paragraphs
            if (   textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull 
                && textDetails.u.full.cAttachedObjects > 0)
            { 
                // Get list of floaters 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 

                floaters = new List(arrayAttachedObjectDesc.Length);

                // Create view results for floaters 
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 

                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient);

                    if(paraClient is FloaterParaClient)
                    { 
                        floaters.Add(paraClient.CreateParagraphResult());
                    } 
                } 
            }
            return (floaters != null && floaters.Count > 0) ? new ReadOnlyCollection(floaters) : null; 
        }

        // ------------------------------------------------------------------
        // Returns a collection of UIElements representing positioned objects. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetFigures() 
        {
            List figures = null; 
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // Floaters are only supported by full paragraphs
            if (   textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull 
                && textDetails.u.full.cAttachedObjects > 0)
            { 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc);
 
                figures = new List(arrayAttachedObjectDesc.Length);

                // Create view results for figures
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++) 
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 
 
                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                    PTS.ValidateHandle(paraClient); 

                    if(paraClient is FigureParaClient)
                    {
                        figures.Add(paraClient.CreateParagraphResult()); 
                    }
                } 
            } 
            return (figures != null && figures.Count > 0) ? new ReadOnlyCollection(figures) : null;
        } 

        // ------------------------------------------------------------------
        // Return TextContentRange for the content of the paragraph.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override TextContentRange GetTextContentRange()
        { 
            PTS.FSTEXTDETAILS textDetails;
            int dcpFirst = 0, dcpLast = 0; 
 
            // Query paragraph details
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");

            dcpFirst = textDetails.u.full.dcpFirst; 
            dcpLast = textDetails.u.full.dcpLim;
 
            // The last TextParaClient has EOP character included, which does not 
            // exist in the tree. Need to remove it.
            // NOTE: cannot remove it when formatting line because PTS does not like empty lines. 
            if (HasEOP && dcpLast > Paragraph.Cch)
            {
                ErrorHandler.Assert(dcpLast == Paragraph.Cch + Line.SyntheticCharacterLength, ErrorHandler.ParagraphCharacterCountMismatch);
                dcpLast -= Line.SyntheticCharacterLength; 
            }
 
            // Text paragraph has always just one range 
            int dcp = Paragraph.ParagraphStartCharacterPosition;
            TextContentRange textContentRange; 

            if(TextParagraph.HasFiguresOrFloaters())
            {
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc = null; 

                int attachedObjectCount = textDetails.u.full.cAttachedObjects; 
                textContentRange = new TextContentRange(); 

                // Recurse into figures and floaters 
                if (attachedObjectCount > 0)
                {
                    // Get list of attached objects
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, attachedObjectCount, out arrayAttachedObjectDesc); 
                }
 
                // Figures and floaters cannot break 
                TextParagraph.UpdateTextContentRangeFromAttachedObjects(textContentRange, dcp + dcpFirst, dcp + dcpLast, arrayAttachedObjectDesc);
 
            }
            else
            {
                textContentRange = new TextContentRange(dcp + dcpFirst, dcp + dcpLast, Paragraph.StructuralCache.TextContainer); 
            }
 
            return textContentRange; 
        }
 
        // -----------------------------------------------------------------
        // Retrieves detailed information about a line of text.
        //
        //     dcpLine - Index of the first character in the line. 
        //     cchContent - Number of content characters in the line.
        //     cchEllipses - Number of content characters hidden by ellipses. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, 
        ///            PtsHelper.LineListSimpleFromTextPara and PtsHelper.LineListCompositeFromTextPara.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void GetLineDetails(int dcpLine, out int cchContent, out int cchEllipses) 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            int lineWidth = 0; 
            bool firstLine = (dcpLine == 0);
            int dcpLim = 0; 
            IntPtr breakRecLine = IntPtr.Zero; 

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc);
 
                        // Get lines information
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                            if (dcpLine == lineDesc.dcpFirst)
                            {
                                lineWidth = lineDesc.dur;
 
                                // Store dcpLim to check that line lengths are in [....]
                                dcpLim = lineDesc.dcpLim; 
 
                                breakRecLine = lineDesc.pfsbreakreclineclient;
 
                                break;
                            }
                        }
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present 
                        PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        // Get lines information
                        int index;
                        for (index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                            if (lineDesc.cElements == 0) continue; 

                            // Get list of line elements. 
                            PTS.FSLINEELEMENT[] arrayLineElement;
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);

                            int elIndex; 
                            for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                            { 
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                                if (element.dcpFirst == dcpLine) 
                                {
                                    lineWidth = element.dur;

                                    // Store dcpLim to check that line lengths are in [....] 
                                    dcpLim = element.dcpLim;
 
                                    breakRecLine = element.pfsbreakreclineclient; 
                                    break;
                                } 
                            }
                            if (elIndex < arrayLineElement.Length)
                            {
                                firstLine = (index == 0); 
                                break;
                            } 
                        } 
                    }
                } 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            // Recreate text line 
            Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache);
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);

            if(IsOptimalParagraph) 
            {
                ctx.LineFormatLengthTarget = dcpLim - dcpLine; 
            } 

            TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); 

            // Assert that number of characters in Text line is the same as our expected length
            Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]");
 
            cchContent = line.ContentLength;
            cchEllipses = line.GetEllipsesLength(); 
 
            line.Dispose();
        } 

        // -----------------------------------------------------------------
        // Retrieves baseline information for first line of text
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, 
        ///            PtsHelper.LineListSimpleFromTextPara and PtsHelper.LineListCompositeFromTextPara. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override int GetFirstTextLineBaseline() 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");

            Rect rect = System.Windows.Rect.Empty;
            int vrBaseline = 0; 

            if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
            { 
                // (a) full with simple lines
                RectFromDcpSimpleLines(0, 0, LogicalDirection.Forward, TextPointerContext.Text, ref textDetails.u.full, ref rect, ref vrBaseline); 
            }
            else
            {
                // (b) full with composite lines - when figures/floaters are present 
                RectFromDcpCompositeLines(0, 0, LogicalDirection.Forward, TextPointerContext.Text, ref textDetails.u.full, ref rect, ref vrBaseline);
            } 
 
            return vrBaseline;
        } 


        // -----------------------------------------------------------------
        // Retrieves ITextPosition for specified character position. 
        //
        //      dcp - Offset from the beginning of the text paragraph. 
        // ------------------------------------------------------------------ 
        internal ITextPointer GetTextPosition(int dcp, LogicalDirection direction)
        { 
            return TextContainerHelper.GetTextPointerFromCP(Paragraph.StructuralCache.TextContainer, dcp + Paragraph.ParagraphStartCharacterPosition, direction);
        }

        // ----------------------------------------------------------------- 
        // Retrieves bounds of an object/character at the specified ITextPointer.
        // 
        //      position - Position of an object/character. 
        //
        // Returns: Bounds of an object/character. 
        // ------------------------------------------------------------------
        /// 
        /// Critical, because calls the Critical functions PTS.FsQueryTextDetails,
        ///     PTS.FsTransformRectangle, RectFromDcpSimpleLines and 
        ///     RectFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to RectFromDcpSimpleLines and
        ///        RectFromDcpCompositeLines is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Rect GetRectangleFromTextPosition(ITextPointer position)
        { 
            Rect rect = System.Windows.Rect.Empty;
 
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)position); 
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition;
            int originalDcp = dcp; 
            if (position.LogicalDirection == LogicalDirection.Backward && dcp > 0)
            {
                --dcp;
            } 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0) 
                {
                    int vrBaseline = 0; 

                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    {
                        // (a) full with simple lines 
                        RectFromDcpSimpleLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline);
                    } 
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        RectFromDcpCompositeLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline);
                    }
                }
            } 
            else
            { 
                // (c) cached - when using ParaChache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }

            // Mirror back to page flow direction
            if(ThisFlowDirection != PageFlowDirection) 
            {
                PTS.FSRECT pageRect = _pageContext.PageRect; 
                PTS.FSRECT rectTransform = new PTS.FSRECT(rect); 
                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform));
                rect = rectTransform.FromTextDpi(); 
            }

            return rect;
        } 

        // ------------------------------------------------------------------ 
        // Returns tight bounding path geometry. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            RectFromDcpSimpleLines and RectFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to RectFromDcpSimpleLines and
        ///        RectFromDcpCompositeLines is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, double paragraphTopSpace, Rect visibleRect) 
        {
            Geometry geometry = null;
            Geometry floatAndFigGeometry = null;
 
            int cpStartTextPointer = startPosition.Offset;
            int cpParagraphStart = Paragraph.ParagraphStartCharacterPosition; 
            int dcpStart = Math.Max(cpStartTextPointer, cpParagraphStart) - cpParagraphStart; 

            int cpEndTextPointer = endPosition.Offset; 
            int cpParagraphEnd = Paragraph.ParagraphEndCharacterPosition;
            int dcpEnd = Math.Min(cpEndTextPointer, cpParagraphEnd) - cpParagraphStart;

            //  apply first line top space only if selection starts before or exactly at this paragraph 
            double firstLineTopSpace = (cpStartTextPointer < cpParagraphStart) ? paragraphTopSpace : 0.0;
 
            //  handle end-of-para only if the range extends beyond this paragraph 
            bool handleEndOfPara = cpEndTextPointer > cpParagraphEnd;
 
            //  mirror transform - needed if flow direction changes
            Transform transform = null;

            if (ThisFlowDirection != PageFlowDirection) 
            {
                transform = new MatrixTransform(-1.0, 0.0, 0.0, 1.0, TextDpi.FromTextDpi(2 * _pageContext.PageRect.u + _pageContext.PageRect.du), 0.0); 
 
                //  (and while we are at it) visibleRect should be mirrored too
                visibleRect = transform.TransformBounds(visibleRect); 
            }

            //  query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaCache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            {
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    { 
                        // (a) full with simple lines
                        geometry = PathGeometryFromDcpRangeSimpleLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect); 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        geometry = PathGeometryFromDcpRangeCompositeLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect);
                    } 
                } 
                //  build highlight for floaters and figures in this paragraph
                if (textDetails.u.full.cAttachedObjects > 0) 
                {
                    floatAndFigGeometry = PathGeometryFromDcpRangeFloatersAndFigures(cpStartTextPointer, cpEndTextPointer, ref textDetails.u.full);
                }
            } 
            else
            { 
                // (c) cached - when using ParaCache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }

            //  at this point geometry contains only the text content related geometry
            if (geometry != null && transform != null) 
            {
                //  mirror back to page flow direction 
                CaretElement.AddTransformToGeometry(geometry, transform); 
            }
 
            //  rectangles from which floatAndFigGeometry is calculated are already mirrored.
            //  this is why geometry and floatAndFigGeometry are combined after geometry is mirrored above
            if (floatAndFigGeometry != null)
            { 
                CaretElement.AddGeometry(ref geometry, floatAndFigGeometry);
            } 
 
            return (geometry);
        } 

        // ------------------------------------------------------------------
        // Returns true if caret is at unit boundary
        // 
        //      position - Position of an object/character.
        // 
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails, 
        ///            IsAtCaretUnitBoundaryFromDcpSimpleLines and
        ///            IsAtCaretUnitBoundaryFromDcpCompositeLines.
        /// Safe - The IntPtr parameter passed to PTS.FsQueryTextDetails is SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to IsAtCaretUnitBoundaryFromDcpSimpleLines and
        ///        IsAtCaretUnitBoundaryFromDcpCompositeLines is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal bool IsAtCaretUnitBoundary(ITextPointer position) 
        {
            bool isAtCaretUnitBoundary = false;

            // Get position offset in paragraph 
            Debug.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer); 
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition; 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        isAtCaretUnitBoundary = IsAtCaretUnitBoundaryFromDcpSimpleLines(dcp, position, ref textDetails.u.full); 
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        isAtCaretUnitBoundary = IsAtCaretUnitBoundaryFromDcpCompositeLines(dcp, position, ref textDetails.u.full);
                    }
                } 
            }
            else 
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            }

            return isAtCaretUnitBoundary; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns next caret unit position
        // 
        //      position - Position of an object/character.
        //      direction - Logical direction in which we seek the position
        //
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails, 
        ///            NextCaretUnitPositionFromDcpSimpleLines and 
        ///            NextCaretUnitPositionFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to the other two Critical functions is
        ///        generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction) 
        { 
            ITextPointer nextCaretPosition = position;
 
            // Get position offset in paragraph
            Debug.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer);
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition; 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines
                        nextCaretPosition = NextCaretUnitPositionFromDcpSimpleLines(dcp, position, direction, ref textDetails.u.full);
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present 
                        nextCaretPosition = NextCaretUnitPositionFromDcpCompositeLines(dcp, position, direction, ref textDetails.u.full);
                    } 
                }
            }
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }
 
            return nextCaretPosition;
        }

        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            BackspaceCaretUnitPositionFromDcpSimpleLines and 
        ///            BackspaceCaretUnitPositionFromDcpCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to BackspaceCaretUnitPositionFromDcpSimpleLines and
        ///        BackspaceCaretUnitPositionFromDcpCompositeLines is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position)
        { 
            ITextPointer backspaceCaretPosition = position; 

            // Get position offset in paragraph 
            Invariant.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer);
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition;
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        backspaceCaretPosition = BackspaceCaretUnitPositionFromDcpSimpleLines(dcp, position, ref textDetails.u.full);
                    }
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        backspaceCaretPosition = BackspaceCaretUnitPositionFromDcpCompositeLines(dcp, position, ref textDetails.u.full); 
                    }
                } 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            return backspaceCaretPosition; 
        }

        // ------------------------------------------------------------------
        // Retrieves a text position given the distance from the beginning 
        // of the line.
        // 
        //      dcpLine - Character offset identifying the line. This is 
        //          the first character position of the line.
        //      distance - Distance from the beginning of the line. 
        //
        // Returns: Text position.
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails,
        ///            LineListSimpleFromTextPara, LineListCompositeFromTextPara and 
        ///            LineElementListFromCompositeLine. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ITextPointer GetTextPositionFromDistance(int dcpLine, double distance) 
        {
            // Query paragraph details 
            int urDistance = TextDpi.ToTextDpi(distance); 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            if(ThisFlowDirection != PageFlowDirection)
            {
                urDistance = _pageContext.PageRect.du - urDistance; 
            }
 
            int lineWidth = 0; 
            bool firstLine = (dcpLine == 0);
            int dcpLim = 0; 
            IntPtr breakRecLine = IntPtr.Zero;

            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    {
                        // (a) full with simple lines 
                        PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 
 
                        // Get lines information
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
                            if (dcpLine == lineDesc.dcpFirst) 
                            {
                                lineWidth = lineDesc.dur; 
                                urDistance -= lineDesc.urStart; 

                                // Store dcpLim to check if line lengths are in [....] 
                                dcpLim = lineDesc.dcpLim;

                                breakRecLine = lineDesc.pfsbreakreclineclient;
 
                                break;
                            } 
                        } 
                    }
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present
                        PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        // Get lines information 
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                            if (lineDesc.cElements == 0) continue;

                            // Get list of line elements. 
                            PTS.FSLINEELEMENT [] arrayLineElement;
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                            int elIndex;
                            for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                            {
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                                if (element.dcpFirst == dcpLine) 
                                {
                                    lineWidth = element.dur; 
                                    urDistance -= element.urStart; 

                                    // Store dcpLim to check if line lengths are in [....] 
                                    dcpLim = element.dcpLim;

                                    breakRecLine = element.pfsbreakreclineclient;
 
                                    break;
                                } 
                            } 
                            if (elIndex < arrayLineElement.Length)
                            { 
                                firstLine = (index == 0);
                                break;
                            }
                        } 
                    }
                } 
            } 
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 

            // Recreate text line 
            Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache); 
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
 
            if(IsOptimalParagraph)
            {
                ctx.LineFormatLengthTarget = dcpLim - dcpLine;
            } 

            TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); 
 
            // Assert that number of characters in Text line is the same as our expected length
            Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]"); 

            CharacterHit charHit = line.GetTextPositionFromDistance(urDistance);
            int cpPosition = charHit.FirstCharacterIndex + charHit.TrailingLength;
            int dcpLastAttachedObject = TextParagraph.GetLastDcpAttachedObjectBeforeLine(dcpLine); 
            if(cpPosition < dcpLastAttachedObject)
            { 
                cpPosition = dcpLastAttachedObject; 
            }
 
            StaticTextPointer pos = TextContainerHelper.GetStaticTextPointerFromCP(Paragraph.StructuralCache.TextContainer, cpPosition + Paragraph.ParagraphStartCharacterPosition);
            LogicalDirection logicalDirection = (charHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
            line.Dispose();
 
            return pos.CreateDynamicTextPointer(logicalDirection);
        } 
 
        // ------------------------------------------------------------------
        // Retrieves collection of GlyphRuns from a range of text. 
        //
        //      glyphRuns - preallocated collection of GlyphRuns. May already
        //          contain runs and new runs need to be appended.
        //      start - the beginning of the range 
        //      end - the end of the range
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            GetGlyphRunsFromSimpleLines and GetGlyphRunsFromCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to GetGlyphRunsFromSimpleLines and
        ///        GetGlyphRunsFromCompositeLines is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void GetGlyphRuns(List glyphRuns, ITextPointer start, ITextPointer end) 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                int dcpStart = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)start) - Paragraph.ParagraphStartCharacterPosition;
                int dcpEnd = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)end) - Paragraph.ParagraphStartCharacterPosition;
                Invariant.Assert(dcpStart >= textDetails.u.full.dcpFirst && dcpEnd <= textDetails.u.full.dcpLim);
 
                if (textDetails.u.full.cLines > 0)
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines 
                        GetGlyphRunsFromSimpleLines(glyphRuns, dcpStart, dcpEnd, ref textDetails.u.full);
                    }
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        GetGlyphRunsFromCompositeLines(glyphRuns, dcpStart, dcpEnd, ref textDetails.u.full); 
                    } 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
        } 

        #endregion Internal Methods 

        // -----------------------------------------------------------------
        //
        //  Internal Properties 
        //
        // ------------------------------------------------------------------ 
 
        #region Internal Properties
 
        // -----------------------------------------------------------------
        // Paragraph associated with this ParaClient.
        // -----------------------------------------------------------------
        internal TextParagraph TextParagraph { get { return (TextParagraph)_paragraph; } } 

        // ----------------------------------------------------------------- 
        // Has EOP character? Is it the last ParaClient of TextParagraph? 
        // ------------------------------------------------------------------
        internal bool HasEOP 
        {
            get { return IsLastChunk; }
        }
 
        // -----------------------------------------------------------------
        // Is this the first chunk of paginated content. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        /// 
        internal override bool IsFirstChunk 
        {
            [SecurityCritical, SecurityTreatAsSafe] 
            get 
            {
                // Query paragraph details 
                PTS.FSTEXTDETAILS textDetails;
                PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");
                // The first chunk always starts with dcpFirst == 0. 
                return (textDetails.u.full.cLines > 0 && textDetails.u.full.dcpFirst == 0);
            } 
        } 

        // ------------------------------------------------------------------ 
        // Is this the last chunk of paginated content.
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///  
        internal override bool IsLastChunk
        { 
            [SecurityCritical, SecurityTreatAsSafe]
            get
            {
                bool lastChunk = false; 

                // Query paragraph details 
                PTS.FSTEXTDETAILS textDetails; 
                PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected."); 

                if (textDetails.u.full.cLines > 0)
                {
                    if (Paragraph.Cch > 0) 
                    {
                        lastChunk = (textDetails.u.full.dcpLim >= Paragraph.Cch); 
                    } 
                    else
                    { 
                        lastChunk = (textDetails.u.full.dcpLim == Line.SyntheticCharacterLength);
                        //

 
                    }
                } 
                return lastChunk; 
            }
        } 

        #endregion Internal Properties

        // ------------------------------------------------------------------ 
        //
        //  Protected Methods 
        // 
        // -----------------------------------------------------------------
 
        #region Protected Methods

        // -----------------------------------------------------------------
        // Arrange paragraph. 
        // -----------------------------------------------------------------
        ///  
        /// Critical, because calls Critical functions PTS.FsQueryTextDetails, 
        ///     PTS.FsQueryFloaterDetails, PTS.FsTransformRectangle and
        ///     some functions on PtsHelper. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnArrange() 
        { 
            base.OnArrange();
 
            // Optimization - Don't arrange if we have no figures, floaters, inline objects
            if(!TextParagraph.HasFiguresFloatersOrInlineObjects())
            {
                return; 
            }
 
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            { 
                // (a) full with simple lines
                // (b) full with composite lines - when figures/floaters are present 
                if (textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        for (int index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                            // Enumerate all inline objects and reformat them. 
                            List inlineObjects = TextParagraph.InlineObjectsFromRange(lineDesc.dcpFirst, lineDesc.dcpLim);
                            if (inlineObjects != null) 
                            { 
                                for (int i = 0; i < inlineObjects.Count; i++)
                                { 
                                    UIElement uiElement = (UIElement)inlineObjects[i].Element;

                                    if(uiElement.IsMeasureValid && !uiElement.IsArrangeValid)
                                    { 
                                        uiElement.Arrange(new Rect(uiElement.DesiredSize));
                                    } 
                                } 
                            }
                        } 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 
 
                        for (int index = 0; index < arrayLineDesc.Length; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];

                            // Get list of line elements
                            PTS.FSLINEELEMENT[] arrayLineElement; 
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                            for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                            {
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                                // Enumerate all inline objects and reformat them.
                                List inlineObjects = TextParagraph.InlineObjectsFromRange(element.dcpFirst, element.dcpLim);
                                if (inlineObjects != null) 
                                {
                                    for (int i = 0; i < inlineObjects.Count; i++) 
                                    { 
                                        UIElement uiElement = (UIElement)inlineObjects[i].Element;
                                        if(uiElement.IsMeasureValid && !uiElement.IsArrangeValid) 
                                        {
                                            uiElement.Arrange(new Rect(uiElement.DesiredSize));
                                        }
                                    } 
                                }
                            } 
                        } 
                    }
                } 

                if (textDetails.u.full.cAttachedObjects > 0)
                {
                    // Get list of floaters 
                    PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 
 
                    // Arrange floaters
 
                    for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                    {
                        PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];
 
                        BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                        PTS.ValidateHandle(paraClient); 
 
                        if(paraClient is FloaterParaClient)
                        { 
                            PTS.FSFLOATERDETAILS floaterDetails;
                            PTS.Validate(PTS.FsQueryFloaterDetails(PtsContext.Context, attachedObjectDesc.pfspara, out floaterDetails));
                            PTS.FSRECT rectFloater = floaterDetails.fsrcFloater;
 
                            if(ThisFlowDirection != PageFlowDirection)
                            { 
                                PTS.FSRECT pageRect = _pageContext.PageRect; 
                                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectFloater, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectFloater));
                            } 

                            ((FloaterParaClient)paraClient).ArrangeFloater(rectFloater, _rect, PTS.FlowDirectionToFswdir(ThisFlowDirection), _pageContext);
                        }
                        else if(paraClient is FigureParaClient) 
                        {
                            PTS.FSFIGUREDETAILS figureDetails; 
                            PTS.Validate(PTS.FsQueryFigureObjectDetails(PtsContext.Context, attachedObjectDesc.pfspara, out figureDetails)); 
                            PTS.FSRECT rectFigure = figureDetails.fsrcFlowAround;
 
                            if(ThisFlowDirection != PageFlowDirection)
                            {
                                PTS.FSRECT pageRect = _pageContext.PageRect;
                                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectFigure, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectFigure)); 
                            }
 
                            ((FigureParaClient)paraClient).ArrangeFigure(rectFigure, _rect, PTS.FlowDirectionToFswdir(ThisFlowDirection), _pageContext); 
                        }
                        else 
                        {
                            Invariant.Assert(false, "Attached object not figure or floater.");
                        }
 
                    }
                } 
            } 
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
        }
 
        #endregion Protected Methods 

        // ------------------------------------------------------------------ 
        //
        //  Private Methods
        //
        // ----------------------------------------------------------------- 

        #region Private Methods 
 
        // ------------------------------------------------------------------
        // Syncs a deferred line visuals list (and update information) with existing visuals 
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical function PtsHelper.LineListSimpleFromTextPara
        ///  
        [SecurityCritical]
        private void SyncUpdateDeferredLineVisuals(VisualCollection lineVisuals, ref PTS.FSTEXTDETAILSFULL textDetails, bool ignoreUpdateInfo) 
        { 
            Debug.Assert(!PTS.ToBoolean(textDetails.fLinesComposite));
 
            try
            {
                if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo)
                { 
                    // _lineIndexFirstVisual will be updated based on the size of this list, so clearing is sufficient here.
                    lineVisuals.Clear(); 
                } 
                else if (_lineIndexFirstVisual != -1)
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
                    PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

                    int lineIndexToBeginRemoval = textDetails.cLinesBeforeChange; 
                    int cLinesToRemove = textDetails.cLinesChanged - textDetails.dcLinesChanged;
                    int insertionIndex = -1; 
 
                    // Shift lines before change
                    if(textDetails.dvrShiftBeforeChange != 0) 
                    {
                        int countVisualsShiftBeforeChange = Math.Min(Math.Max(lineIndexToBeginRemoval - _lineIndexFirstVisual, 0), lineVisuals.Count);
                        for(int index = 0; index < countVisualsShiftBeforeChange; index++)
                        { 
                            // Shift line's visual
                            ContainerVisual lineVisual = (ContainerVisual) lineVisuals[index]; 
                            Vector offset = lineVisual.Offset; 
                            offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange);
                            lineVisual.Offset = offset; 
                        }
                    }

                    // If the line index to begin removal is before our first visual, then the overlap will look like 
                    //      |---------------|  (Committed visual range)
                    // |------|                (Range to remove) 
                    if (lineIndexToBeginRemoval < _lineIndexFirstVisual) 
                    {
                        // Determine the amount of overlap, and remove. 
                        int actualLinesToRemove = Math.Min(Math.Max(lineIndexToBeginRemoval - _lineIndexFirstVisual + cLinesToRemove, 0), lineVisuals.Count);

                        if (actualLinesToRemove > 0)
                        { 
                            lineVisuals.RemoveRange(0, actualLinesToRemove);
                        } 
 
                        if (lineVisuals.Count == 0)
                        { 
                            lineVisuals.Clear();
                            _lineIndexFirstVisual = -1;
                        }
                        else 
                        {
                            insertionIndex = 0; 
                            _lineIndexFirstVisual = lineIndexToBeginRemoval; 
                        }
                    } 
                    else if (lineIndexToBeginRemoval < _lineIndexFirstVisual + lineVisuals.Count)
                    {
                        // Else case for overlap
                        //  |---------------|  (Committed visual range) 
                        //       |-----|                (Range to remove)
                        // Or 
                        //  |---------------| 
                        //           |--------------|
 
                        // Removing from the middle
                        int actualLinesToRemove = Math.Min(cLinesToRemove, lineVisuals.Count - (lineIndexToBeginRemoval - _lineIndexFirstVisual));

                        lineVisuals.RemoveRange(lineIndexToBeginRemoval - _lineIndexFirstVisual, actualLinesToRemove); 

                        insertionIndex = lineIndexToBeginRemoval - _lineIndexFirstVisual; // Insertion index is relative to committed visual range 
                    } 

                    int shiftIndex = -1; 

                    if (insertionIndex != -1)
                    {
                        // Add new lines 
                        // Insertion must occur at some point along our committed visual range
                        Debug.Assert(insertionIndex >= 0 && insertionIndex <= lineVisuals.Count); 
 
                        for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                            ContainerVisual lineVisual = CreateLineVisual(ref arrayLineDesc[index], Paragraph.ParagraphStartCharacterPosition);
 
                            lineVisuals.Insert(insertionIndex + (index - textDetails.cLinesBeforeChange), lineVisual);
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        } 

                        shiftIndex = insertionIndex + textDetails.cLinesChanged; 
                    }

                    // Any committed visuals after our inserted section must be shifted
                    if (shiftIndex != -1) 
                    {
                        // Shift remaining lines 
                        for (int index = shiftIndex; index < lineVisuals.Count; index++) 
                        {
                            // Shift line's visual 
                            ContainerVisual lineVisual = (ContainerVisual) lineVisuals[index];
                            Vector offset = lineVisual.Offset;
                            offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange);
                            lineVisual.Offset = offset; 
                        }
                    } 
                } 
            }
 
            finally
            {
                // If no visuals, committed range is nonexistant, so -1
                if (lineVisuals.Count == 0) 
                {
                    _lineIndexFirstVisual = -1; 
                } 
            }
 
#if VERIFY_VISUALS
            // Verify our visuals are in-[....] with the actual line visuals.
            VerifyVisuals(ref textDetails);
#endif 
        }
 
        // ----------------------------------------------------------------- 
        // Retrieve lines from simple lines.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical, because:
        ///     a) calls Critical function PtsHelper.LineListSimpleFromTextPara
        ///     b) calls Critical function PTS.FsTransformRectangle 
        /// 
        [SecurityCritical] 
        private ReadOnlyCollection LineResultsFromSimpleLines(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            List lines = new List(arrayLineDesc.Length); 
 
            // Get lines information
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                Rect lbox = new Rect( 
                    TextDpi.FromTextDpi(lineDesc.urBBox), TextDpi.FromTextDpi(lineDesc.vrStart),
                    TextDpi.FromTextDpi(lineDesc.durBBox), TextDpi.FromTextDpi(lineDesc.dvrAscent + lineDesc.dvrDescent)); 
 
                // Mirror layout box to page flow direction
                if(PageFlowDirection != ThisFlowDirection) 
                {
                    PTS.FSRECT pageRect = _pageContext.PageRect;
                    PTS.FSRECT rectTransform = new PTS.FSRECT(lbox);
                    PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                    lbox = rectTransform.FromTextDpi();
                } 
 
                lines.Add(new TextParaLineResult(this, lineDesc.dcpFirst, lineDesc.dcpLim - lineDesc.dcpFirst,
                    lbox, TextDpi.FromTextDpi(lineDesc.dvrAscent))); 
            }

            if (lines.Count != 0)
            { 
                // Hide EOP character
                TextParaLineResult lastLineResult = (TextParaLineResult)lines[lines.Count - 1]; 
                if (HasEOP && lastLineResult.DcpLast > Paragraph.Cch) 
                {
                    ErrorHandler.Assert(lastLineResult.DcpLast - Line.SyntheticCharacterLength == Paragraph.Cch, ErrorHandler.ParagraphCharacterCountMismatch); 
                    lastLineResult.DcpLast -= Line.SyntheticCharacterLength;
                }
            }
 
            return (lines.Count > 0) ? new ReadOnlyCollection(lines) : null;
        } 
 
        // -----------------------------------------------------------------
        // Retrieve lines from composite lines. 
        // -----------------------------------------------------------------
        /// 
        /// Critical, because:
        ///     a) calls Critical function PtsHelper.LineListCompositeFromTextPara 
        ///     b) calls Critical function PtsHelper.LineElementListFromCompositeLine.
        ///     c) calls Critical function PTS.FsTransformRectangle 
        ///  
        [SecurityCritical]
        private ReadOnlyCollection LineResultsFromCompositeLines(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            // Get list of complex composite lines. 
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            List lines = new List(arrayLineDesc.Length);
 
            // Get lines information
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }
 
                // Get list of line elements. 
                PTS.FSLINEELEMENT [] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 

                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // Create line info 
                    Rect lbox = new Rect(TextDpi.FromTextDpi(element.urBBox), TextDpi.FromTextDpi(lineDesc.vrStart), 
                                         TextDpi.FromTextDpi(element.durBBox), TextDpi.FromTextDpi(element.dvrAscent + element.dvrDescent));
 
                    // Mirror layout box to page flow direction
                    if(ThisFlowDirection != PageFlowDirection)
                    {
                        PTS.FSRECT pageRect = _pageContext.PageRect; 
                        PTS.FSRECT rectTransform = new PTS.FSRECT(lbox);
                        PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                        lbox = rectTransform.FromTextDpi(); 
                    }
 
                    lines.Add(new TextParaLineResult(this, element.dcpFirst, element.dcpLim - element.dcpFirst,
                        lbox, TextDpi.FromTextDpi(element.dvrAscent)));
                }
            } 

            if (lines.Count != 0) 
            { 
                // Hide EOP character
                TextParaLineResult lastLineResult = (TextParaLineResult)lines[lines.Count - 1]; 
                if (HasEOP && lastLineResult.DcpLast > Paragraph.Cch)
                {
                    ErrorHandler.Assert(lastLineResult.DcpLast - Line.SyntheticCharacterLength == Paragraph.Cch, ErrorHandler.ParagraphCharacterCountMismatch);
                    lastLineResult.DcpLast -= Line.SyntheticCharacterLength; 
                }
            } 
 
            return (lines.Count > 0) ? new ReadOnlyCollection(lines) : null;
        } 

        // -----------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara 
        ///  
        [SecurityCritical]
        private void RectFromDcpSimpleLines( 
            int dcp,
            int originalDcp,
            LogicalDirection orientation,
            TextPointerContext context, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            ref Rect rect, 
            ref int vrBaseline) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line.
                // But if this is the last line (EOP character), get rectangle form the last 
                // character of the line.
                if (   ((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) 
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);

                    if(IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Get rect from cp
                    FlowDirection flowDirection; 
                    rect = line.GetBoundsFromTextPosition(dcp, out flowDirection); 
                    rect.X += TextDpi.FromTextDpi(lineDesc.urStart);
                    rect.Y += TextDpi.FromTextDpi(lineDesc.vrStart); 

                    // Return only TopLeft and Height.
                    // Adjust rect.Left by taking into account flow direction of the
                    // content and orientation of input position. 
                    if (ThisFlowDirection != flowDirection)
                    { 
                        if (orientation == LogicalDirection.Forward) 
                        {
                            rect.X = rect.Right; 
                        }
                    }
                    else
                    { 
                        // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning
                        //       content with Backward orientation. This should not be a valid position. 
                        //       Remove it later 
                        // We also need to check here if the context is an inline element, such as a hidden run. In such a
                        // case we will have the rect of the character immediately following the hidden run, which is the same as 
                        // originalDcp. If we take the right bounds of the rect case we will be off by one character.
                        if (orientation == LogicalDirection.Backward && originalDcp > 0 && (context == TextPointerContext.Text || context == TextPointerContext.EmbeddedElement))
                        {
                            rect.X = rect.Right; 
                        }
                    } 
                    rect.Width = 0; 

                    vrBaseline = line.Baseline + lineDesc.vrStart; 

                    // Dispose the line
                    line.Dispose();
                    break; 
                }
            } 
        } 

        // ----------------------------------------------------------------- 
        // Retrieve bounds of an object/character at specified text position.
        // ------------------------------------------------------------------
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        ///  
        [SecurityCritical]
        private void RectFromDcpCompositeLines( 
            int dcp,
            int originalDcp,
            LogicalDirection orientation,
            TextPointerContext context, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            ref Rect rect, 
            ref int vrBaseline) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT [] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line.
                    // But if this is the last line (EOP character), get rectangle form the last 
                    // character of the line.
                    if (   ((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) 
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    {
                        // Create and format line 
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);

                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Get rect from cp
                        FlowDirection flowDirection; 
                        rect = line.GetBoundsFromTextPosition(dcp, out flowDirection); 
                        rect.X += TextDpi.FromTextDpi(element.urStart);
                        rect.Y += TextDpi.FromTextDpi(lineDesc.vrStart); 

                        // Return only TopLeft and Height.
                        // Adjust rect.Left by taking into account flow direction of the
                        // content and orientation of input position. 
                        if (ThisFlowDirection != flowDirection)
                        { 
                            if (orientation == LogicalDirection.Forward) 
                            {
                                rect.X = rect.Right; 
                            }
                        }
                        else
                        { 
                            // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning
                            //       content with Backward orientation. This should not be a valid position. 
                            //       Remove it later 
                            if (orientation == LogicalDirection.Backward && originalDcp > 0 && (context == TextPointerContext.Text || context == TextPointerContext.EmbeddedElement))
                            { 
                                rect.X = rect.Right;
                            }
                        }
                        rect.Width = 0; 

                        vrBaseline = line.Baseline + lineDesc.vrStart; 
 
                        // Dispose the line
                        line.Dispose(); 
                        break;
                    }
                }
            } 
        }
 
        // ----------------------------------------------------------------- 
        // Returns tight bounding path geometry for simple lines
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical] 
        private Geometry PathGeometryFromDcpRangeSimpleLines(
            int dcpStart, 
            int dcpEnd, 
            double paragraphTopSpace,
            bool handleEndOfPara, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            Rect visibleRect)
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            Geometry geometry = null; 
 
            //  get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            int lineStart = 0;
            int lineCount = arrayLineDesc.Length; 

            if(_lineIndexFirstVisual != -1) 
            { 
                lineStart = _lineIndexFirstVisual;
                lineCount = _visual.Children.Count; 
            }

            for (int lineIndex = lineStart; lineIndex < (lineStart + lineCount); ++lineIndex)
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex];
 
                if (handleEndOfPara) 
                {
                    //  Note (end-of-para workaround): '<' gives the chance 
                    //  for the EOP handling code below to emulate EOP glyph
                    if (dcpEnd < lineDesc.dcpFirst)
                    {
                        //  this line starts after the range's end. 
                        //  safe to break from the loop.
                        break; 
                    } 
                }
                else 
                {
                    if (dcpEnd <= lineDesc.dcpFirst)
                    {
                        //  this line starts after the range's end. 
                        //  safe to break from the loop.
                        break; 
                    } 
                }
 
                //  'dcp' needs to be within line range. If position points to dcpLim,
                //  it means that the next line starts from such position, hence go to the next line.
                //  But if this is the last line (EOP character), get geometry form the last
                //  character of the line. 
                if (    lineDesc.dcpLim > dcpStart
                    ||  (   (lineIndex == arrayLineDesc.Length - 1) 
                        &&  (lineDesc.dcpLim == dcpStart)   ) 
                   )
                { 
                    int dcpRangeStartForThisLine = Math.Max(lineDesc.dcpFirst, dcpStart);
                    //  Note (end-of-para workaround): dcp can be '0' due to end-of-para
                    //  not included into cp count - but it is there!!!
                    int cchRangeForThisLine = Math.Max(Math.Min(lineDesc.dcpLim, dcpEnd) - dcpRangeStartForThisLine, 1); 
                    double lineTopSpace = (lineIndex == 0) ? paragraphTopSpace : 0.0;
                    double endOfParaGlyphWidth; 
 
                    if (    (handleEndOfPara && lineIndex == (arrayLineDesc.Length - 1))
                        ||  (dcpEnd >= lineDesc.dcpLim && HasAnyLineBreakAtCp(lineDesc.dcpLim)) ) 
                    {
                        endOfParaGlyphWidth = ((double)TextParagraph.Element.GetValue(TextElement.FontSizeProperty) * CaretElement.c_endOfParaMagicMultiplier);
                    }
                    else 
                    {
                        endOfParaGlyphWidth = 0; 
                    } 

                    //  get rectangles for this line. 
                    IList rectangles = RectanglesFromDcpRangeOfSimpleLine(
                            dcpRangeStartForThisLine,
                            cchRangeForThisLine,
                            lineTopSpace, 
                            endOfParaGlyphWidth,
                            ref lineDesc, 
                            lineIndex, 
                            visibleRect
                            ); 

                    if (rectangles != null)
                    {
                        for (int i = 0, count = rectangles.Count; i < count; ++i) 
                        {
                            RectangleGeometry rectGeometry = new RectangleGeometry(rectangles[i]); 
                            CaretElement.AddGeometry(ref geometry, rectGeometry); 
                        }
                    } 
                }
            }

            return geometry; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns tight bounding path geometry for composite lines
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine.
        ///  
        [SecurityCritical]
        private Geometry PathGeometryFromDcpRangeCompositeLines( 
            int dcpStart, 
            int dcpEnd,
            double paragraphTopSpace, 
            bool handleEndOfPara,
            ref PTS.FSTEXTDETAILSFULL textDetails,
            Rect visibleRect)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            Geometry geometry = null; 

            //  get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            for (int lineIndex = 0; lineIndex < arrayLineDesc.Length; ++lineIndex) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[lineIndex]; 
                if (lineDesc.cElements == 0) 
                {
                    continue; 
                }

                //  get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elemIndex = 0; elemIndex < arrayLineElement.Length; ++elemIndex) 
                {
                    PTS.FSLINEELEMENT elemDesc = arrayLineElement[elemIndex]; 

                    if (handleEndOfPara)
                    {
                        //  Note (end-of-para workaround): '<' gives the chance 
                        //  for the EOP handling code below to emulate EOP glyph
                        if (dcpEnd < elemDesc.dcpFirst) 
                        { 
                            //  this line starts after the range's end.
                            //  safe to break from the loop. 
                            break;
                        }
                    }
                    else 
                    {
                        if (dcpEnd <= elemDesc.dcpFirst) 
                        { 
                            //  this line starts after the range's end.
                            //  safe to break from the loop. 
                            break;
                        }
                    }
 
                    //  'dcp' needs to be within line range. If position points to dcpLim,
                    //  it means that the next line starts from such position, hence go to the next line. 
                    //  But if this is the last line (EOP character), get geometry form the last 
                    //  character of the line.
                    if (    elemDesc.dcpLim > dcpStart 
                        ||  (   (elemDesc.dcpLim == dcpStart)
                            &&  (elemIndex == arrayLineElement.Length - 1)
                            &&  (lineIndex == arrayLineDesc.Length - 1) )   )
                    { 
                        int dcpRangeStartForThisElem = Math.Max(elemDesc.dcpFirst, dcpStart);
                        //  Note (end-of-para workaround): dcp can be '0' due to end-of-para 
                        //  not included into cp count - but it is there!!! 
                        int cchRangeForThisElem = Math.Max(Math.Min(elemDesc.dcpLim, dcpEnd) - dcpRangeStartForThisElem, 1);
                        double lineTopSpace = (lineIndex == 0) ? paragraphTopSpace : 0.0; 
                        double endOfParaGlyphWidth;

                        if (    (handleEndOfPara && lineIndex == (arrayLineDesc.Length - 1))
                            ||  (dcpEnd >= elemDesc.dcpLim && HasAnyLineBreakAtCp(elemDesc.dcpLim)  )   ) 
                        {
                            endOfParaGlyphWidth = ((double)TextParagraph.Element.GetValue(TextElement.FontSizeProperty) * CaretElement.c_endOfParaMagicMultiplier); 
                        } 
                        else
                        { 
                            endOfParaGlyphWidth = 0;
                        }

                        //  get rectangles for this element. 
                        IList rectangles = RectanglesFromDcpRangeOfCompositeLineElement(
                                dcpRangeStartForThisElem, 
                                cchRangeForThisElem, 
                                lineTopSpace,
                                endOfParaGlyphWidth, 
                                ref lineDesc,
                                lineIndex,
                                ref elemDesc,
                                elemIndex, 
                                visibleRect
                                ); 
 
                        if (rectangles != null)
                        { 
                            for (int i = 0, count = rectangles.Count; i < count; ++i)
                            {
                                RectangleGeometry rectGeometry = new RectangleGeometry(rectangles[i]);
                                CaretElement.AddGeometry(ref geometry, rectGeometry); 
                            }
                        } 
                    } 
                }
            } 

            return geometry;
        }
 
        // -----------------------------------------------------------------
        //  Helper to check if there is any line break at the given dcp. 
        //  Dcp adjusted to this paragraph. 
        // ------------------------------------------------------------------
        private bool HasAnyLineBreakAtCp(int dcp) 
        {
            ITextPointer position = Paragraph.StructuralCache.TextContainer.CreatePointerAtOffset(Paragraph.ParagraphStartCharacterPosition + dcp, LogicalDirection.Forward);
            //
            return (TextPointerBase.IsNextToAnyBreak(position, LogicalDirection.Backward)); 
        }
 
        // ----------------------------------------------------------------- 
        //  Returns rectangles for a single simple line correcsponding to the
        //  given dcp range. Includes trailing whitespaces. 
        //  Params:
        //      dcpRangeStart     - range's cp start position. Adjusted for
        //                          line's cp range.
        //      cchRange          - nuber of cps in the range. 
        //      lineTopSpace      - the value that line's height should
        //                          be extended to at the top. 
        //      lineRightSpace    - the value that line's width should 
        //                          be extended to at the right.
        //      lineDesc          - line description. 
        //      lineIndex         - line index.
        //      visibleRect       - visibility rectangle. It is Ok to return
        //                          null if the line is not visible.
        //      hasAttachedObjects- attached objects are present. 
        //  Returns:
        //      null              - if line is not visible 
        //      rectangles        - otherwise. 
        // ------------------------------------------------------------------
        private List RectanglesFromDcpRangeOfSimpleLine( 
            int dcpRangeStart,
            int cchRange,
            double lineTopSpace,
            double lineRightSpace, 
            ref PTS.FSLINEDESCRIPTIONSINGLE lineDesc,
            int lineIndex, 
            Rect visibleRect) 
        {
            List rectangles = null; 

            Invariant.Assert(lineDesc.dcpFirst <= dcpRangeStart && dcpRangeStart <= lineDesc.dcpLim && cchRange > 0);

            Rect lineRect = new PTS.FSRECT(lineDesc.urBBox, lineDesc.vrStart, lineDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); 

            //  width has to be adjusted to include trailing whitespaces... 
            LineVisual lineVisual = FetchLineVisual(lineIndex); 
            if (lineVisual != null)
            { 
                lineRect.Width = lineVisual.WidthIncludingTrailingWhitespace;
            }

            lineRect.Y = lineRect.Y - lineTopSpace; 
            lineRect.Height = lineRect.Height + lineTopSpace;
            lineRect.Width = lineRect.Width + lineRightSpace; 
 
            // Ignore horizontal offset because TextBox page width != extent width.
            // It's ok to include content that doesn't strictly intersect -- this 
            // is a perf optimization and the edge cases won't significantly hurt us.
            Rect testRect = lineRect;
            testRect.X = visibleRect.X;
 
            if (testRect.IntersectsWith(visibleRect))
            { 
                // Check whether the line is fully selected - we don't need to reformat it in this case 
                if (dcpRangeStart == lineDesc.dcpFirst && lineDesc.dcpLim <= (dcpRangeStart + cchRange))
                { 
                    rectangles = new List(1);
                    rectangles.Add(lineRect);
                }
                else 
                {
                    //  create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    if (IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 
 
                    double duOffset = TextDpi.FromTextDpi(lineDesc.urStart);
                    double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart); 

                    rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset);

                    if (!DoubleUtil.IsZero(lineTopSpace)) 
                    {
                        for (int i = 0, count = rectangles.Count; i < count; ++i) 
                        { 
                            Rect r = rectangles[i];
                            r.Y = r.Y - lineTopSpace; 
                            r.Height = r.Height + lineTopSpace;
                            rectangles[i] = r;
                        }
                    } 

                    if (!DoubleUtil.IsZero(lineRightSpace)) 
                    { 
                        // add the rect representing end-of-line / end-of-para
                        rectangles.Add( 
                            new Rect(
                                duOffset + TextDpi.FromTextDpi(line.Start + line.Width),
                                dvOffset - lineTopSpace,
                                lineRightSpace, 
                                TextDpi.FromTextDpi(line.Height) + lineTopSpace
                                ) 
                            ); 
                    }
 
                    //  dispose of the line
                    line.Dispose();
                }
            } 

            return (rectangles); 
        } 

        // ------------------------------------------------------------------ 
        //  Returns rectangles for a single composite line correcsponding to the
        //  given dcp range. Includes trailing whitespaces.
        //  Params:
        //      dcpRangeStart     - range's cp start position. Adjusted for 
        //                          line's cp range.
        //      cchRange          - nuber of cps in the range. 
        //      lineTopSpace      - the value that line's height should 
        //                          be extended to at the top.
        //      lineRightSpace    - the value that line's width should 
        //                          be extended to at the right.
        //      lineDesc          - line description.
        //      lineIndex         - line index.
        //      elemDesc          - element description. 
        //      visibleRect       - visibility rectangle. It is Ok to return
        //                          null if the line is not visible. 
        //      hasAttachedObjects- Attached objects are present 
        //  Returns:
        //      null              - if line is not visible 
        //      rectangles        - otherwise.
        // -----------------------------------------------------------------
        private List RectanglesFromDcpRangeOfCompositeLineElement(
            int dcpRangeStart, 
            int cchRange,
            double lineTopSpace, 
            double lineRightSpace, 
            ref PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc,
            int lineIndex, 
            ref PTS.FSLINEELEMENT elemDesc,
            int elemIndex,
            Rect visibleRect)
        { 
            List rectangles = null;
 
            Rect elementRect = new PTS.FSRECT(elemDesc.urBBox, lineDesc.vrStart, elemDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); 

            //  width has to be adjusted to include trailing whitespaces... 
            LineVisual lineVisual = FetchLineVisualComposite(lineIndex, elemIndex);
            if (lineVisual != null)
            {
                elementRect.Width = lineVisual.WidthIncludingTrailingWhitespace; 
            }
 
            elementRect.Y = elementRect.Y - lineTopSpace; 
            elementRect.Height = elementRect.Height + lineTopSpace;
            elementRect.Width = elementRect.Width + lineRightSpace; 

            // Ignore horizontal offset because TextBox page width != extent width.
            // It's ok to include content that doesn't strictly intersect -- this
            // is a perf optimization and the edge cases won't significantly hurt us. 
            Rect testRect = elementRect;
            testRect.X = visibleRect.X; 
 
            if (testRect.IntersectsWith(visibleRect))
            { 
                // Check whether the line is fully selected - we don't need to reformat it in this case
                if (dcpRangeStart == elemDesc.dcpFirst && elemDesc.dcpLim <= (dcpRangeStart + cchRange))
                {
                    rectangles = new List(1); 
                    rectangles.Add(elementRect);
                } 
                else 
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(elemDesc.fClearOnLeft), PTS.ToBoolean(elemDesc.fClearOnRight), TextParagraph.TextRunCache);
                    if (IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = elemDesc.dcpLim - elemDesc.dcpFirst;
                    } 
                    TextParagraph.FormatLineCore(line, elemDesc.pfsbreakreclineclient, ctx, elemDesc.dcpFirst, elemDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), elemDesc.dcpFirst); 
                    Invariant.Assert(line.SafeLength == elemDesc.dcpLim - elemDesc.dcpFirst, "Line length is out of [....]");
 
                    double duOffset = TextDpi.FromTextDpi(elemDesc.urStart);
                    double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart);

                    rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset); 

                    if (!DoubleUtil.IsZero(lineTopSpace)) 
                    { 
                        for (int i = 0, count = rectangles.Count; i < count; ++i)
                        { 
                            Rect r = rectangles[i];
                            r.Y = r.Y - lineTopSpace;
                            r.Height = r.Height + lineTopSpace;
                            rectangles[i] = r; 
                        }
                    } 
 
                    if (!DoubleUtil.IsZero(lineRightSpace))
                    { 
                        // add the rect representing end-of-line / end-of-para
                        rectangles.Add(
                            new Rect(
                                duOffset + TextDpi.FromTextDpi(line.Start + line.Width), 
                                dvOffset - lineTopSpace,
                                lineRightSpace, 
                                TextDpi.FromTextDpi(line.Height) + lineTopSpace 
                                )
                            ); 
                    }

                    //  dispose of the line
                    line.Dispose(); 
                }
            } 
 
            return (rectangles);
        } 

        // ------------------------------------------------------------------
        //  Helper to return visual corresponding to a line in
        //  FSLINEDESCRIPTIONSINGLE array. 
        // -----------------------------------------------------------------
        private LineVisual FetchLineVisual(int index) 
        { 
            LineVisual visual = null;
 
            int count = VisualTreeHelper.GetChildrenCount(Visual);

            if (count != 0)
            { 
                int visualIndex = index;
 
                if (_lineIndexFirstVisual != -1) 
                {
                    visualIndex -= _lineIndexFirstVisual; 
                }

                if (0 <= visualIndex && visualIndex < count)
                { 
                    visual = VisualTreeHelper.GetChild(Visual, visualIndex) as LineVisual;
                    //  verify that our assumptions about visual structure is correct... 
                    Invariant.Assert(visual != null || VisualTreeHelper.GetChild(Visual, visualIndex) == null); 
                }
            } 

            return (visual);
        }
 
        // -----------------------------------------------------------------
        //  Helper to return visual corresponding to a line in 
        //  FSLINEDESCRIPTIONSINGLE array. 
        // -----------------------------------------------------------------
        private LineVisual FetchLineVisualComposite(int lineIndex, int elemIndex) 
        {
            LineVisual visual = null;
            Visual temp = Visual;
            int count = VisualTreeHelper.GetChildrenCount(Visual); 

            if (count != 0) 
            { 
                int visualIndex = lineIndex;
 
                if(VisualTreeHelper.GetChild(Visual, visualIndex) is ParagraphElementVisual)
                {
                    temp = Visual.InternalGetVisualChild(lineIndex);
                    visualIndex = elemIndex; 
                }
 
                visual = VisualTreeHelper.GetChild(temp, visualIndex) as LineVisual; 
                //  verify that our assumptions about visual structure is correct...
                Invariant.Assert(visual != null || VisualTreeHelper.GetChild(temp, visualIndex) == null); 
            }

            return (visual);
        } 

        // ------------------------------------------------------------------ 
        // Returns tight bounding path geometry for 
        // floaters and figures objects
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.AttachedObjectListFromParagraph
        /// 
        [SecurityCritical] 
        private Geometry PathGeometryFromDcpRangeFloatersAndFigures(
            int dcpStart, 
            int dcpEnd, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            Geometry geometry = null;

            if (textDetails.cAttachedObjects > 0)
            { 
                //  get list of attached objects
                PTS.FSATTACHEDOBJECTDESCRIPTION[] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.cAttachedObjects, out arrayAttachedObjectDesc); 

                for (int index = 0; index < arrayAttachedObjectDesc.Length; ++index) 
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];

                    BaseParaClient objectParaClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(objectParaClient);
 
                    BaseParagraph objectPara = objectParaClient.Paragraph; 

                    if (dcpEnd <= objectPara.ParagraphStartCharacterPosition) 
                    {
                        break;
                    }
 
                    if (objectPara.ParagraphEndCharacterPosition > dcpStart)
                    { 
                        Rect objectRect = objectParaClient.Rect.FromTextDpi(); 
                        RectangleGeometry objectGeometry = new RectangleGeometry(objectRect);
                        CaretElement.AddGeometry(ref geometry, objectGeometry); 
                    }
                }
            }
 
            return (geometry);
        } 
 
        // ------------------------------------------------------------------
        // Return true if caret is at unit boundary 
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara.
        ///  
        [SecurityCritical]
        private bool IsAtCaretUnitBoundaryFromDcpSimpleLines( 
            int dcp, 
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            bool isAtCaretUnitBoundary = false;
 
            // First iterate through lines
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                // 'dcp' needs to be within line range. If position points to dcpLim, 
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                {
                    CharacterHit charHit = new CharacterHit();
                    if (dcp >= lineDesc.dcpLim - 1 && index == arrayLineDesc.Length - 1)
                    { 
                        // Special case: last line has additional character to mark the end of paragraph.
                        // We should not try and check for next source character index 
                        // But just return true in this case 
                        return true;
                    } 

                    if (position.LogicalDirection == LogicalDirection.Backward)
                    {
                        if (lineDesc.dcpFirst == dcp) 
                        {
                            if (index == 0) 
                            { 
                                // First position of first line does not have a trailing edge. Return false.
                                return false; 
                            }
                            else
                            {
                                // Get the trailing edge of the last character on the previous line, at dcp - 1 
                                index--;
                                lineDesc = arrayLineDesc[index]; 
                                Invariant.Assert(dcp > 0); 
                                charHit = new CharacterHit(dcp - 1, 1);
                            } 
                        }
                        else
                        {
                            // Get CharacterHit at trailing edge of previous position 
                            Invariant.Assert(dcp > 0);
                            charHit = new CharacterHit(dcp - 1, 1); 
                        } 
                    }
                    else if (position.LogicalDirection == LogicalDirection.Forward) 
                    {
                        // Get character hit at leading edge
                        charHit = new CharacterHit(dcp, 0);
                    } 

                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
 
                    if(IsOptimalParagraph)
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 
 
                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 
                    isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit);

                    // Dispose the line
                    line.Dispose(); 
                    break;
                } 
            } 
            return isAtCaretUnitBoundary;
        } 

        // -----------------------------------------------------------------
        // Return true if caret is at unit boundary
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        ///  
        [SecurityCritical]
        private bool IsAtCaretUnitBoundaryFromDcpCompositeLines( 
            int dcp,
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            bool isAtCaretUnitBoundary = false;

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0)
                { 
                    continue;
                }

                // Get list of line elements. 
                PTS.FSLINEELEMENT[] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                { 
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line. 
                    if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp))
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    { 
                        CharacterHit charHit = new CharacterHit();
                        if (dcp >= element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) 
                        {
                            // Special case: at the end of the last line there is a special character that
                            // does not belong to the line.  Return true for this case
                            return true; 
                        }
 
                        if (position.LogicalDirection == LogicalDirection.Backward) 
                        {
                            // Beginning of element. 
                            if (dcp == element.dcpFirst)
                            {
                                if (elIndex > 0)
                                { 
                                    // Beginning of element, but not of line. Create char hit at last dcp of previous element, trailing edge.
                                    --elIndex; 
                                    element = arrayLineElement[elIndex]; 
                                    charHit = new CharacterHit(dcp - 1, 1);
                                } 
                                else
                                {
                                    // Beginning of line
                                    if (index == 0) 
                                    {
                                        // Backward context at start position of first line is not considered a unit boundary 
                                        return false; 
                                    }
                                    else 
                                    {

                                        // Go to previous line
                                        --index; 
                                        lineDesc = arrayLineDesc[index];
                                        if (lineDesc.cElements == 0) 
                                        { 
                                            return false;
                                        } 
                                        else
                                        {
                                            // Get list of line elements.
                                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
                                            element = arrayLineElement[arrayLineElement.Length - 1];
                                            charHit = new CharacterHit(dcp - 1, 1); 
                                        } 
                                    }
                                } 
                            }
                            else
                            {
                                // Get trailing edge of previous dcp 
                                Invariant.Assert(dcp > 0);
                                charHit = new CharacterHit(dcp - 1, 1); 
                            } 
                        }
                        else if (position.LogicalDirection == LogicalDirection.Forward) 
                        {
                            // Create character hit at leading edge
                            charHit = new CharacterHit(dcp, 0);
                        } 

                        // Create and format line 
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
 
                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 
 
                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 
                        isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit);

                        // Dispose the line
                        line.Dispose(); 
                        return isAtCaretUnitBoundary;
                    } 
                } 
            }
            return isAtCaretUnitBoundary; 
        }

        // -----------------------------------------------------------------
        // Get Next caret unit position 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer NextCaretUnitPositionFromDcpSimpleLines(
            int dcp,
            ITextPointer position,
            LogicalDirection direction, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare next position and set it to initial position 
            ITextPointer nextCaretPosition = position;
 
            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                { 
                    if (dcp == lineDesc.dcpFirst && direction == LogicalDirection.Backward)
                    { 
                        // Go to previous line
                        if (index == 0)
                        {
                            return position; 
                        }
                        else 
                        { 
                            // Update dcp, lineDesc
                            Debug.Assert(index > 0); 
                            --index;
                            lineDesc = arrayLineDesc[index];
                        }
                    } 
                    else if (dcp >= lineDesc.dcpLim - 1 && direction == LogicalDirection.Forward)
                    { 
                        // If we are at the last line there will be a fake marker for this, so we return 
                        if (index == arrayLineDesc.Length - 1)
                        { 
                            return position;
                        }
                    }
 
                    // Create and format line
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

                    if(IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);
 
                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Create CharacterHit
                    CharacterHit charHit = new CharacterHit(dcp, 0);

                    // Get previous caret position from the line 
                    // Determine logical direction for next caret index and create TextPointer from it
                    CharacterHit nextCharacterHit; 
                    if (direction == LogicalDirection.Forward) 
                    {
                        nextCharacterHit = line.GetNextCaretCharacterHit(charHit); 
                    }
                    else
                    {
                        nextCharacterHit = line.GetPreviousCaretCharacterHit(charHit); 
                    }
 
                    LogicalDirection logicalDirection; 
                    if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == lineDesc.dcpLim) && direction == LogicalDirection.Forward)
                    { 
                        // Going forward brought us to the end of a line, context must be forward for next line
                        if (index == arrayLineDesc.Length - 1)
                        {
                            // last line so context must stay backward 
                            logicalDirection = LogicalDirection.Backward;
                        } 
                        else 
                        {
                            logicalDirection = LogicalDirection.Forward; 
                        }
                    }
                    else if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == lineDesc.dcpFirst) && direction == LogicalDirection.Backward)
                    { 
                        // Going forward brought us to the start of a line, context must be backward for previous line
                        if (index == 0) 
                        { 
                            // First line, so we will stay forward
                            logicalDirection = LogicalDirection.Forward; 
                        }
                        else
                        {
                            logicalDirection = LogicalDirection.Backward; 
                        }
                    } 
                    else 
                    {
                        logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; 
                    }
                    nextCaretPosition = GetTextPosition(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection);

                    // Dispose the line 
                    line.Dispose();
                    break; 
                } 
            }
            return nextCaretPosition; 
        }

        // -----------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer NextCaretUnitPositionFromDcpCompositeLines(
            int dcp,
            ITextPointer position,
            LogicalDirection direction, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare next position and set it to initial position 
            ITextPointer nextCaretPosition = position;
 
            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements. 
                PTS.FSLINEELEMENT [] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                { 
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line. 
                    if (   ((element.dcpFirst <= dcp) && (element.dcpLim > dcp))
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    { 
                        if (dcp == element.dcpFirst && direction == LogicalDirection.Backward)
                        { 
                            // Beginning of element.
                            if (dcp == 0)
                            {
                                // Assert that this is first elment on first line 
                                Debug.Assert(index == 0);
                                Debug.Assert(elIndex == 0); 
                                return position; 
                            }
                            else 
                            {
                                if (elIndex > 0)
                                {
                                    // Beginning of element, but not of line 
                                    --elIndex;
                                    element = arrayLineElement[elIndex]; 
                                } 
                                else
                                { 
                                    // There must be at least one line above this
                                    Debug.Assert(index > 0);

                                    // Go to previous line 
                                    --index;
                                    lineDesc = arrayLineDesc[index]; 
                                    if (lineDesc.cElements == 0) 
                                    {
                                        // Stay in same position 
                                        return position;
                                    }
                                    else
                                    { 

                                        // Get list of line elements. 
                                        PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
                                        element = arrayLineElement[arrayLineElement.Length - 1];
                                    } 
                                }
                            }
                        }
                        else if (dcp >= element.dcpLim - 1 && direction == LogicalDirection.Forward) 
                        {
                            if (dcp == element.dcpLim) 
                            { 
                                // End of paragraph
                                Debug.Assert(elIndex == arrayLineElement.Length - 1); 
                                Debug.Assert(index == arrayLineDesc.Length - 1);
                                return position;
                            }
                            else if (dcp == element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) 
                            {
                                // Special end character does not belong to line 
                                return position; 
                            }
                        } 

                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                        if(IsOptimalParagraph) 
                        { 
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Create CharacterHit from dcp, and get next 
                        CharacterHit charHit = new CharacterHit(dcp, 0);
                        CharacterHit nextCharacterHit; 
                        if (direction == LogicalDirection.Forward)
                        {
                            nextCharacterHit = line.GetNextCaretCharacterHit(charHit);
                        } 
                        else
                        { 
                            nextCharacterHit = line.GetPreviousCaretCharacterHit(charHit); 
                        }
 
                        LogicalDirection logicalDirection;
                        if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == element.dcpLim) && direction == LogicalDirection.Forward)
                        {
                            // Going forward brought us to the end of a line, context must be forward for next line 
                            if (index == arrayLineDesc.Length - 1)
                            { 
                                // last line so context must stay backward 
                                logicalDirection = LogicalDirection.Backward;
                            } 
                            else
                            {
                                // It is a new element, on the same line or a new one. Either way it;s forward context
                                logicalDirection = LogicalDirection.Forward; 
                            }
                        } 
                        else if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == element.dcpFirst) && direction == LogicalDirection.Backward) 
                        {
                            // Going forward brought us to the start of a line, context must be backward for previous line 
                            if (index == 0)
                            {
                                // First line, so we will stay forward
                                logicalDirection = LogicalDirection.Forward; 
                            }
                            else 
                            { 
                                // Either the previous element or last element on previous line, context is backward
                                logicalDirection = LogicalDirection.Backward; 
                            }
                        }
                        else
                        { 
                            logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
                        } 
                        nextCaretPosition = GetTextPosition(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection); 

                        // Dispose the line 
                        line.Dispose();
                        return nextCaretPosition;
                    }
                } 
            }
            return nextCaretPosition; 
        } 

        // ----------------------------------------------------------------- 
        // Get Backspace caret unit position
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer BackspaceCaretUnitPositionFromDcpSimpleLines( 
            int dcp,
            ITextPointer position, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // Declare backspace position and set it to initial position 
            ITextPointer backspaceCaretPosition = position;

            // First iterate through lines
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
 
                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1)))
                {
                    if (dcp == lineDesc.dcpFirst) 
                    {
                        // Go to previous line 
                        if (index == 0) 
                        {
                            return position; 
                        }
                        else
                        {
                            // Update dcp, lineDesc 
                            Debug.Assert(index > 0);
                            --index; 
                            lineDesc = arrayLineDesc[index]; 
                        }
                    } 

                    // Create and format line
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

                    if(IsOptimalParagraph) 
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Create CharacterHit and get backspace index from line API 
                    CharacterHit textSourceCharacterIndex = new CharacterHit(dcp, 0);
                    CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex); 
                    LogicalDirection logicalDirection;
                    if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == lineDesc.dcpFirst)
                    {
                        // Going forward brought us to the start of a line, context must be backward for previous line 
                        if (index == 0)
                        { 
                            // First line, so we will stay forward 
                            logicalDirection = LogicalDirection.Forward;
                        } 
                        else
                        {
                            logicalDirection = LogicalDirection.Backward;
                        } 
                    }
                    else 
                    { 
                        logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
                    } 
                    backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection);

                    // Dispose the line
                    line.Dispose(); 
                    break;
                } 
            } 
            Debug.Assert(backspaceCaretPosition != null);
            return backspaceCaretPosition; 
        }

        // ------------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer BackspaceCaretUnitPositionFromDcpCompositeLines(
            int dcp,
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare backspace position and set it to initial position
            ITextPointer backspaceCaretPosition = position; 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line.
                    if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) 
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1)))
                    { 
                        if (dcp == element.dcpFirst) 
                        {
                            // Beginning of element. 
                            if (dcp == 0)
                            {
                                // Assert that this is first elment on first line
                                Debug.Assert(index == 0); 
                                Debug.Assert(elIndex == 0);
                                return position; 
                            } 
                            else
                            { 
                                if (elIndex > 0)
                                {
                                    // Beginning of element, but not of line
                                    --elIndex; 
                                    element = arrayLineElement[elIndex];
                                } 
                                else 
                                {
                                    // There must be at least one line above this 
                                    Debug.Assert(index > 0);

                                    // Go to previous line
                                    --index; 
                                    lineDesc = arrayLineDesc[index];
                                    if (lineDesc.cElements == 0) 
                                    { 
                                        // Stay in same position
                                        return position; 
                                    }
                                    else
                                    {
                                        // Get list of line elements. 
                                        PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
                                        element = arrayLineElement[arrayLineElement.Length - 1]; 
                                    } 
                                }
                            } 
                        }

                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
 
                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);
 
                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 
 
                        // Create CharacterHit from dcp, and get backspace
                        CharacterHit charHit = new CharacterHit(dcp, 0); 
                        CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(charHit);
                        LogicalDirection logicalDirection;
                        if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == element.dcpFirst)
                        { 
                            // Going forward brought us to the start of a line, context must be backward for previous line
                            if (index == 0) 
                            { 
                                // First line, so we will stay forward
                                logicalDirection = LogicalDirection.Forward; 
                            }
                            else
                            {
                                logicalDirection = LogicalDirection.Backward; 
                            }
                        } 
                        else 
                        {
                            logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; 
                        }
                        backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection);

                        // Dispose the line 
                        line.Dispose();
                        return backspaceCaretPosition; 
                    } 
                }
            } 
            return backspaceCaretPosition;
        }

        // ------------------------------------------------------------------ 
        // Retrieves collection of GlyphRuns from a range of text.
        // ----------------------------------------------------------------- 
        ///  
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        ///  
        [SecurityCritical]
        private void GetGlyphRunsFromSimpleLines(
            List glyphRuns,
            int dcpStart, 
            int dcpEnd,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Iterate through all lines
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                // Range (dcpStart...dcpEnd) needs to insersect with line's range.
                if (dcpStart < lineDesc.dcpLim && dcpEnd > lineDesc.dcpFirst)
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Retrieve glyphs from this line 
                    line.GetGlyphRuns(glyphRuns, Math.Max(dcpStart, lineDesc.dcpFirst), Math.Min(dcpEnd, lineDesc.dcpLim));

                    // Dispose the line
                    line.Dispose(); 
                }
                // No need to continue, if dcpEnd has been reached. 
                if (dcpEnd < lineDesc.dcpLim) 
                    break;
            } 
        }

        // -----------------------------------------------------------------
        // Retrieves collection of GlyphRuns from a range of text. 
        // -----------------------------------------------------------------
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical]
        private void GetGlyphRunsFromCompositeLines(
            List glyphRuns, 
            int dcpStart,
            int dcpEnd, 
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // Range (dcpStart...dcpEnd) needs to insersect with line's range.
                    if (dcpStart < element.dcpLim && dcpEnd > element.dcpFirst)
                    { 
                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        }
 
                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);
 
                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Retrieve glyphs from this line
                        line.GetGlyphRuns(glyphRuns, Math.Max(dcpStart, element.dcpFirst), Math.Min(dcpEnd, element.dcpLim));

                        // Dispose the line 
                        line.Dispose();
                    } 
                    // No need to continue, if dcpEnd has been reached. 
                    if (dcpEnd < element.dcpLim)
                        break; 
                }
            }
        }
 
        // -----------------------------------------------------------------
        // Render text paragraph content. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara 
        /// 
        [SecurityCritical]
        private void RenderSimpleLines(
            ContainerVisual visual, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            bool ignoreUpdateInfo) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; 

            VisualCollection visualChildren = visual.Children;

            // Get list of simple lines. 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            // Create lines and render them
            if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo) 
            {
                // There is no update information, hence need to recreate
                // visuals for all lines.
                visualChildren.Clear(); 

                for (int index = 0; index < arrayLineDesc.Length; index++) 
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                    // Create and format line
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Create and validate line's visual 
                    ContainerVisual lineVisual = line.CreateVisual(); 
                    visualChildren.Insert(index, lineVisual);
                    lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                    // Dispose the line
                    line.Dispose();
                } 
            }
            else 
            { 
                // Shift lines before change
                if(textDetails.dvrShiftBeforeChange != 0) 
                {
                    for(int index = 0; index < textDetails.cLinesBeforeChange; index++)
                    {
                        ContainerVisual lineVisual = (ContainerVisual) visualChildren[index]; 
                        Vector offset = lineVisual.Offset;
                        offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange); 
                        lineVisual.Offset = offset; 
                    }
                } 

                // Skip not changed lines
                // Remove changed lines.
                visualChildren.RemoveRange(textDetails.cLinesBeforeChange, textDetails.cLinesChanged - textDetails.dcLinesChanged); 
                // Add new lines
                for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++) 
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                    // Create and format line
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Create and validate line's visual 
                    ContainerVisual lineVisual = line.CreateVisual(); 
                    visualChildren.Insert(index, lineVisual);
                    lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                    // Dispose the line
                    line.Dispose();
                } 
                // Shift remaining lines
                for (int index = textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index < arrayLineDesc.Length; index++) 
                { 
                    // Shift line's visual
                    ContainerVisual lineVisual = (ContainerVisual) visualChildren[index]; 
                    Vector offset = lineVisual.Offset;
                    offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange);
                    lineVisual.Offset = offset;
                } 
            }
        } 
 

        // ------------------------------------------------------------------ 
        // Determines whether this para intersects with the rect on v dimension
        // -----------------------------------------------------------------
        private bool IntersectsWithRectOnV(ref PTS.FSRECT rect)
        { 
            return ((_rect.v) <= (rect.v + rect.dv)) &&
                   ((_rect.v + _rect.dv) >= rect.v); 
        } 

        // ------------------------------------------------------------------ 
        // Determines whether this para is totally contained in the given rect on v dimension
        // -----------------------------------------------------------------
        private bool ContainedInRectOnV(ref PTS.FSRECT rect)
        { 
            return (rect.v <= (_rect.v)) &&
                   (rect.v + rect.dv >= (_rect.v + _rect.dv)); 
        } 

        // ----------------------------------------------------------------- 
        // From a line desc and the cp for the para start, formats a line and creates a visual
        // -----------------------------------------------------------------
        private ContainerVisual CreateLineVisual(ref PTS.FSLINEDESCRIPTIONSINGLE lineDesc, int cpTextParaStart)
        { 
            Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 
 
            if(IsOptimalParagraph)
            { 
                ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
            }

            TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

            // Assert that number of characters in Text line is the same as our expected length 
            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

            // Create and validate line's visual 
            ContainerVisual lineVisual = line.CreateVisual();

            // Dispose the line
            line.Dispose(); 

            return lineVisual; 
        } 

        // ------------------------------------------------------------------ 
        // Resyncs the committed visuals section with actual viewport visibility
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private void UpdateViewportSimpleLines( 
            ContainerVisual visual,
            ref PTS.FSTEXTDETAILSFULL textDetails, 
            ref PTS.FSRECT viewport)
        {
            VisualCollection visualChildren = visual.Children;
 
            Debug.Assert(!PTS.ToBoolean(textDetails.fLinesComposite));
 
            try 
            {
                // Common case, invisible para - Clear our children, _lineIndexFirstVisual will be cleared later 
                if (!IntersectsWithRectOnV(ref viewport))
                {
                    visualChildren.Clear();
                } 
                else if (ContainedInRectOnV(ref viewport) && _lineIndexFirstVisual == 0 && visualChildren.Count == textDetails.cLines)
                { 
                    // Totally visible para 
                    // Nothing to do here, totally visible and lines are updated. Don't query line list
                } 
                else
                {
                    // Index of first visible line
                    int lineIndexFirstVisible = -1; 
                    // Index of first invisible line - MAY BE EQUAL TO COUNT.
                    int lineIndexFirstInvisible = -1; 
                    int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; 

                    PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
                    PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

                    // If this para is totally contained in the viewport, valid range is all lines.
                    if (ContainedInRectOnV(ref viewport)) 
                    {
                        lineIndexFirstVisible = 0; 
                        lineIndexFirstInvisible = textDetails.cLines; 
                    }
                    else 
                    {
                        // Subset is valid, walk the lines to determine the first (even partially) visible line
                        int lineIndex;
 
                        for (lineIndex = 0; lineIndex < arrayLineDesc.Length; lineIndex++)
                        { 
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex]; 

                            // Vrstart is top of line, not baseline. 
                            if ((lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent) > viewport.v)
                            {
                                break;
                            } 
                        }
 
                        // May be equal to count if no lines are visible 
                        lineIndexFirstVisible = lineIndex;
 
                        // Subset is valid, walk the lines to determine the first totally invisible line
                        for (lineIndex = lineIndexFirstVisible; lineIndex < arrayLineDesc.Length; lineIndex++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex]; 

                            if ((lineDesc.vrStart) > (viewport.v + viewport.dv)) 
                            { 
                                break;
                            } 
                        }

                        // May be equal to count if the remainder of lines is visible.
                        lineIndexFirstInvisible = lineIndex; 
                    }
 
                    // If we have some committed range, but there's no overlap between the previously committed range and the new desired range, 
                    // Delete all existing lines.
                    if (_lineIndexFirstVisual != -1 && ((lineIndexFirstVisible > _lineIndexFirstVisual + visualChildren.Count) || 
                                                       (lineIndexFirstInvisible < _lineIndexFirstVisual)))
                    {
                        visualChildren.Clear();
                        _lineIndexFirstVisual = -1; 
                    }
 
                    // If no existing lines, interate over visible range and add appropriate lines. 
                    if (_lineIndexFirstVisual == -1)
                    { 
                        for (int index = lineIndexFirstVisible; index < lineIndexFirstInvisible; index++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                            ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart);
                            visualChildren.Add(lineVisual); 
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        }
                        _lineIndexFirstVisual = lineIndexFirstVisible; 
                    }
                    else if (lineIndexFirstVisible != _lineIndexFirstVisual || (lineIndexFirstInvisible - lineIndexFirstVisible) != visualChildren.Count)
                    {
                        // Need to resolve existing list with new list - [....] the beginning of the list 

 
                        //    |----------------| (Old committed range) 
                        // |-------|             (New committed range)
                        // Need to add visuals to the beginning to [....] the start position 
                        if (lineIndexFirstVisible < _lineIndexFirstVisual)
                        {
                            for (int index = lineIndexFirstVisible; index < _lineIndexFirstVisual; index++)
                            { 
                                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                                ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart); 

                                visualChildren.Insert(index - lineIndexFirstVisible, lineVisual); 
                                lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
                            }
                        }
                        else if (lineIndexFirstVisible != _lineIndexFirstVisual) 
                        {
                            //    |----------------| (Old committed range) 
                            //        |-------|             (New committed range) 
                            // Need to remove visuals from the beginning to [....] start positions.
 
                            visualChildren.RemoveRange(0, lineIndexFirstVisible - _lineIndexFirstVisual);
                        }
                        _lineIndexFirstVisual = lineIndexFirstVisible;
                    } 

                    Debug.Assert(_lineIndexFirstVisual == lineIndexFirstVisible); 
 

                    // Now [....] the end of the list, two cases.. 
                    // Fewer lines than existed before, remove lines from end
                    // |---------------|
                    // |----------|
                    if (lineIndexFirstInvisible - lineIndexFirstVisible < visualChildren.Count) 
                    {
                        int visualsToRemove = visualChildren.Count - (lineIndexFirstInvisible - lineIndexFirstVisible); 
 
                        visualChildren.RemoveRange(visualChildren.Count - visualsToRemove, visualsToRemove);
                    } 
                    else if ((lineIndexFirstInvisible - lineIndexFirstVisible) > visualChildren.Count)
                    {
                        // Or we need to add more lines to the end, format and insert those visuals
                        // |--------------| 
                        // |----------------------|
 
                        for (int index = _lineIndexFirstVisual + visualChildren.Count; index < lineIndexFirstInvisible; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                            ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart);
                            visualChildren.Add(lineVisual);
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        }
                    } 
 
                    Debug.Assert(visualChildren.Count == (lineIndexFirstInvisible - lineIndexFirstVisible));
                } 
            }

            finally
            { 
                // Ensure the _lineIndexFirstVisual is syncced with visualChildren.Count
                if (visualChildren.Count == 0) 
                { 
                    _lineIndexFirstVisual = -1;
                } 
            }

#if VERIFY_VISUALS
            // Verify visuals against visuals list. 
            VerifyVisuals(ref textDetails);
#endif 
        } 

#if VERIFY_VISUALS 

        // Verifies all of our committed visuals are in the right order and agree with the locations of all of the full text details visuals
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListSimpleFromTextPara 
        /// Safe - The IntPtr parameters passed to PtsHelper.LineListSimpleFromTextPara are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails coming as input parameter and is passed as is.  But this is still secure 
        ///        because of the way it's used inside PtsHelper.LineListSimpleFromTextPara. It's only
        //         used to create an array of size textDetails.cLines. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void VerifyVisuals(ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ContainerVisual lineContainerVisual = _visual;
 
            // Add visuals for floaters and figures. 
            if (textDetails.cFigures > 0 || textDetails.cFloaters > 0)
            { 
                lineContainerVisual = _visual.Children[0];
            }

            VisualCollection visualChildren = lineContainerVisual.Children; 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            if(_lineIndexFirstVisual == -1)
            { 
                Debug.Assert(visualChildren.Count == 0);
            }
            else
            { 
                Debug.Assert(visualChildren.Count > 0);
 
                for(int index = _lineIndexFirstVisual; index < _lineIndexFirstVisual + visualChildren.Count; index++) 
                {
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                    Visual visualChild = visualChildren[index - _lineIndexFirstVisual];

                    Vector offset = visualChild.Offset;
                    int u = TextDpi.ToTextDpi(offset.X); 
                    int v = TextDpi.ToTextDpi(offset.Y);
 
                    Debug.Assert(u == (lineDesc.urStart) && v == (lineDesc.vrStart)); 
                }
            } 
        }

#endif
 
        // ------------------------------------------------------------------
        // Render composite lines. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara 
        ///            and PtsHelper.LineElementListFromCompositeLine.
        /// 
        [SecurityCritical]
        private void RenderCompositeLines( 
            ContainerVisual visual,
            ref PTS.FSTEXTDETAILSFULL textDetails, 
            bool ignoreUpdateInfo) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            VisualCollection visualChildren = visual.Children;
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;
 
            // Get list of composite lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // Create lines and render them 
            if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo)
            {
                // There is no update information, hence need to recreate
                // visuals for all lines. 
                visualChildren.Clear();
 
                for (int index = 0; index < arrayLineDesc.Length; index++) 
                {
                    PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 

                    int visualIndex;
                    VisualCollection lineVisuals;
                    if (lineDesc.cElements == 1) 
                    {
                        visualIndex = index; 
                        lineVisuals = visualChildren; 
                    }
                    else 
                    {
                        visualIndex = 0;
                        ParagraphElementVisual lineVisual = new ParagraphElementVisual();
                        visualChildren.Add(lineVisual); 
                        lineVisuals = lineVisual.Children;
                    } 
 
                    // Get list of line elements
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);

                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                    { 
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];
 
                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 

                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }
 
                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                        // Create and validate line's visual
                        ContainerVisual lineVisual = line.CreateVisual(); 
                        lineVisuals.Insert(visualIndex + elIndex, lineVisual);
                        lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
 
                        // Dispose the line
                        line.Dispose(); 
                    }
                }
            }
            else 
            {
                // Shift lines before change 
                if(textDetails.dvrShiftBeforeChange != 0) 
                {
                    for (int index = 0; index < textDetails.cLinesBeforeChange; index++) 
                    {
                        ContainerVisual lineVisual = (ContainerVisual) visualChildren[index];
                        Vector offset = lineVisual.Offset;
                        offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange); 
                        lineVisual.Offset = offset;
                    } 
                } 

                // Skip not changed lines 
                // Remove changed lines
                visualChildren.RemoveRange(textDetails.cLinesBeforeChange, textDetails.cLinesChanged - textDetails.dcLinesChanged);
                // Add new lines
                for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++) 
                {
                    PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
 
                    int visualIndex;
                    VisualCollection lineVisuals; 
                    if (lineDesc.cElements == 1)
                    {
                        visualIndex = index;
                        lineVisuals = visualChildren; 
                    }
                    else 
                    { 
                        visualIndex = 0;
                        ParagraphElementVisual lineVisual = new ParagraphElementVisual(); 
                        visualChildren.Add(lineVisual);
                        lineVisuals = lineVisual.Children;
                    }
 
                    // Get list of line elements.
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 

                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                    {
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 
 
                        if(IsOptimalParagraph)
                        { 
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        }

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 

                        // Create and validate line's visual 
                        ContainerVisual lineVisual = line.CreateVisual();
                        lineVisuals.Insert(visualIndex + elIndex, lineVisual);
                        lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
 
                        // Dispose the line
                        line.Dispose(); 
                    } 
                }
                // Shift remaining lines 
                for (int index = textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index < arrayLineDesc.Length; index++)
                {
                    ContainerVisual lineVisual = (ContainerVisual)visualChildren[index];
 
                    Vector offset = lineVisual.Offset;
                    offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange); 
                    lineVisual.Offset = offset; 
                }
            } 
        }

        // -----------------------------------------------------------------
        // Render floaters and figures. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls some Critical PtsHelper functions. 
        /// Safe - as the IntPtr parameters passed in are Critical for set.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ValidateVisualFloatersAndFigures(
            PTS.FSKUPDATE fskupdInherited,
            int cAttachedObjects) 
        {
            int index; 
            PTS.FSKUPDATE fskupd; 

            if (cAttachedObjects > 0) 
            {
                BaseParaClient paraClient;

                // Get list of floaters 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, cAttachedObjects, 
                                                          out arrayAttachedObjectDesc); 

                // Render floaters. For each floater do following: 
                // (1) Retrieve ParaClient object
                // (2) Validate visual, if necessary
                for (index = 0; index < arrayAttachedObjectDesc.Length; index++)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];
 
                    paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient);
 
                    fskupd = attachedObjectDesc.fsupdinf.fskupd;
                    if (fskupd == PTS.FSKUPDATE.fskupdInherited)
                    {
                        fskupd = fskupdInherited; 
                    }
 
                    if(fskupd != PTS.FSKUPDATE.fskupdNoChange) 
                    {
                        paraClient.ValidateVisual(fskupd); 
                    }
                }
            }
        } 

        // ----------------------------------------------------------------- 
        // Hittest simple lines. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical]
        private IInputElement InputHitTestSimpleLines( 
            PTS.FSPOINT pt,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            IInputElement ie = null;
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;

            // Get list of complex lines. 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            // Find affected line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                if (lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent > pt.v) 
                {
                    // Create and format line 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

 
                    using (line) 
                    {
                        TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                        if (lineDesc.urStart + line.CalculateUOffsetShift() <= pt.u && pt.u <= (lineDesc.urStart + line.CalculateUOffsetShift() + lineDesc.dur))
                        { 
                            int distance = pt.u - lineDesc.urStart; 

                            // Assert that number of characters in Text line is the same as our expected length 
                            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");

                            if ((line.Start <= distance) && (distance <= (line.Start + line.Width)))
                            { 
                                ie = line.InputHitTest(distance);
                            } 
                        } 
                    }
                    break; 
                }
            }
            return ie;
        } 

        // ----------------------------------------------------------------- 
        // We don't support deferred visuals in finite case, composite lines case, or figures/floaters/inline objects case 
        // ------------------------------------------------------------------
        private bool IsDeferredVisualCreationSupported(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            if(!Paragraph.StructuralCache.IsDeferredVisualCreationSupported)
                return false;
 
            if(PTS.ToBoolean(textDetails.fLinesComposite))
                return false; 
 
            if(TextParagraph.HasFiguresFloatersOrInlineObjects())
                return false; 


            return true;
        } 

        // ----------------------------------------------------------------- 
        // GetRectangles in simple lines 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical]
        private List GetRectanglesInSimpleLines( 
            ContentElement e,
            int start, 
            int length, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            List rectangles = new List();
 
            // Calculate local element start by subtracting TextPara start relative to TextContainer
            // from element start 
            int localStart = start - Paragraph.ParagraphStartCharacterPosition; 

            if (localStart < 0) 
            {
                // May happen in case of figures, floaters
                return rectangles;
            } 

            // Get list of complex lines. 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                List lineRectangles = GetRectanglesInSingleLine(lineDesc, e, localStart, length);
                Invariant.Assert(lineRectangles != null); 
                if (lineRectangles.Count != 0) 
                {
                    rectangles.AddRange(lineRectangles); 
                }
            }
            return rectangles;
        } 

        // ------------------------------------------------------------------ 
        // Returns ArrayList of rectangles (at msot one) for the given ContentElement 
        // e if it spans all or part of the specified line
        // ----------------------------------------------------------------- 
        private List GetRectanglesInSingleLine(
            PTS.FSLINEDESCRIPTIONSINGLE lineDesc,
            ContentElement e,
            int start, 
            int length)
        { 
            // Calculate end of element relative to TextParagraph by adding length to start position 
            int end = start + length;
            List rectangles = new List(); 

            // If the element does not lie in the line at all, return empty list
            if (start >= lineDesc.dcpLim)
            { 
                // Element starts after line ends
                return rectangles; 
            } 
            if (end <= lineDesc.dcpFirst)
            { 
                // Element ends before line starts
                return rectangles;
            }
 
            // Establish start and end points of element span within the line so that
            // we can get rectangle between them 
            int localStart = (start < lineDesc.dcpFirst) ? lineDesc.dcpFirst : start; 
            int localEnd = (end < lineDesc.dcpLim) ? end : lineDesc.dcpLim;
            Debug.Assert(localEnd > localStart); 

            // Create and format line
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
            Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

            if(IsOptimalParagraph) 
            { 
                ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
            } 

            TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

            // Assert that number of characters in Text line is the same as our expected length 
            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
            // Get rectangles from start and end positions of range 
            rectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
 
            // Rectangles must have at least one element
            Invariant.Assert(rectangles.Count > 0);

            // Dispose the line 
            line.Dispose();
 
            return rectangles; 
        }
 
        // ------------------------------------------------------------------
        // Hittest composite lines.
        // -----------------------------------------------------------------
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara 
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical] 
        private IInputElement InputHitTestCompositeLines(
            PTS.FSPOINT pt,
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            IInputElement ie = null; 
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;
 
            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected composite line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) continue; 

                if (lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent > pt.v)
                {
                    // Affected composite line has been found. 

                    // Get list of line elements. 
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                    // increment is unreachable (loop exits after first iteration) because of 'break' - bug 1788192
                    #pragma warning disable 0162
                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                    { 
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];
 
                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 

                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }
 
 
                        using (line)
                        { 
                            TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);

                            // Assert that number of characters in Text line is the same as our expected length
                            Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 

                            if (element.urStart + line.CalculateUOffsetShift() <= pt.u && pt.u <= (element.urStart + line.CalculateUOffsetShift() + element.dur)) 
                            { 
                                int distance = pt.u - element.urStart;
 
                                // Assert that number of characters in Text line is the same as our expected length
                                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                                // Assert that number of characters in Text line is the same as our expected length 
                                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                                if ((line.Start <= distance) && (distance <= (line.Start + line.Width))) 
                                {
                                    ie = line.InputHitTest(distance); 
                                }
                            }
                        }
                        break; 
                    }
                    #pragma warning restore 0162 
 
                    break;
                } 
            }

            return ie;
        } 

        // ----------------------------------------------------------------- 
        // Get Rectangles in composite lines. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical functions PtsHelper.LineListCompositeFromTextPara.
        /// 
        [SecurityCritical]
        private List GetRectanglesInCompositeLines( 
            ContentElement e,
            int start, 
            int length, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            List rectangles = new List();
            int localStart = start - Paragraph.ParagraphStartCharacterPosition; 

            if (localStart < 0) 
            { 
                // May happen in case of figures, floaters
                return rectangles; 
            }

            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected composite line by looking at vertical offset 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) continue;

                List lineRectangles = GetRectanglesInCompositeLine(lineDesc, e, localStart, length); 
                Invariant.Assert(lineRectangles != null);
 
                if (lineRectangles.Count != 0) 
                {
                    rectangles.AddRange(lineRectangles); 
                }
            }

            return rectangles; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns ArrayList of rectangles (at msot one) for the given ContentElement
        // e if it spans all or part of the specified line 
        // ------------------------------------------------------------------
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical function PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical] 
        private List GetRectanglesInCompositeLine( 
            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc,
            ContentElement e, 
            int start,
            int length)
        {
            List rectangles = new List(); 
            int end = start + length;
 
            // If the element does not lie in the line at all, return empty list 
            //if (start > lineDesc.dcpLim)
            //{ 
            //    // Element starts after line ends
            //    return rectangles;
            //}
            //if (end < lineDesc.dcpFirst) 
            //{
                // Element ends before line starts 
            //    return rectangles; 
            //}
 
            // Get list of line elements.
            PTS.FSLINEELEMENT[] arrayLineElement;
            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
            for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
            { 
                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                // Check if element we are looking for does not span the current element at all 
                if (start >= element.dcpLim)
                {
                    // Element starts after other element ends
                    continue; 
                }
                if (end <= element.dcpFirst) 
                { 
                    // Element ends before line starts
                    continue; 
                }
                // Establish start and end points of element span within the line so that
                // we can get rectangle between them
                int localStart = (start < element.dcpFirst) ? element.dcpFirst : start; 
                int localEnd = (end < element.dcpLim) ? end : element.dcpLim;
                Debug.Assert(localEnd > localStart); 
 
                Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                if(IsOptimalParagraph)
                {
                    ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                }
 
                TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                // Assert that number of characters in Text line is the same as our expected length 
                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                // Get rectangles from start and end positions of range for this element
                List elementRectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                // Rectangles must have at least one element 
                Invariant.Assert(elementRectangles.Count > 0); 

                // Add rectangles from this element to rectangles from whole line 
                rectangles.AddRange(elementRectangles);

                // Dispose the line
                line.Dispose(); 
            }
            return rectangles; 
        } 

        #endregion Private Methods 

        #region Private Properties

        private bool IsOptimalParagraph { get { return TextParagraph.IsOptimalParagraph; } } 

        #endregion 
 
        //-------------------------------------------------------------------
        // 
        //  Private Fields
        //
        //--------------------------------------------------------------------
 
        #region Private Fields
 
        private int _lineIndexFirstVisual = -1; 

        #endregion Private Fields 
    }
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: ContainerParagraph.cs 
//
// Description: TextParagClient is responsible for handling display related 
//              data of text paragraphs. 
//
// History: 
//  05/05/2003 : grzegorz - moving from Avalon branch.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Diagnostics; 
using System.Security;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.TextFormatting; 
using System.Windows.Documents;
using MS.Internal; 
using MS.Internal.Documents; 
using MS.Internal.Text;
 
using MS.Internal.PtsHost.UnsafeNativeMethods;

namespace MS.Internal.PtsHost
{ 
    // ---------------------------------------------------------------------
    // TextParaClient is responsible for handling display related data of 
    // text paragraphs. 
    // ---------------------------------------------------------------------
    internal sealed class TextParaClient : BaseParaClient 
    {
        // ------------------------------------------------------------------
        // Constructor.
        // 
        //      paragraph - Paragraph associated with this object.
        // ----------------------------------------------------------------- 
        internal TextParaClient(TextParagraph paragraph) : base(paragraph) 
        {
        } 

        // ------------------------------------------------------------------
        //
        //  Internal Methods 
        //
        // ------------------------------------------------------------------ 
 
        #region Internal Methods
 
        // -----------------------------------------------------------------
        // Validate visual node associated with paragraph.
        //
        //      fskupdInherited - inherited update info 
        //      fswdir - inherited flow direction
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            RenderSimpleLines and RenderCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to RenderSimpleLines and RenderCompositeLines
        ///        is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) 
        {
            // Query paragraph details and render its content 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            VisualCollection visualChildren = _visual.Children; 
            ContainerVisual lineContainerVisual = _visual;
 
            bool ignoreUpdateInfo = false; 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (visualChildren.Count != 0 && !(visualChildren[0] is LineVisual))
                { 
                    visualChildren.Clear(); 
                    ignoreUpdateInfo = true;
                } 


                if(IsDeferredVisualCreationSupported(ref textDetails.u.full))
                { 
                    // Transition to from no deferred visuals to deferred visuals -- Ignore update info
                    if(_lineIndexFirstVisual == -1 && lineContainerVisual.Children.Count > 0) 
                    { 
                        ignoreUpdateInfo = true;
                    } 

                    SyncUpdateDeferredLineVisuals(lineContainerVisual.Children, ref textDetails.u.full, ignoreUpdateInfo);
                }
                else 
                {
                    // Transition from deferred visuals to no deferred visuals -- Ignore update info 
                    if(_lineIndexFirstVisual != -1) 
                    {
                        _lineIndexFirstVisual = -1; 
                        lineContainerVisual.Children.Clear();
                    }

                    // If we have no children, update isn't really possible. 
                    if(lineContainerVisual.Children.Count == 0)
                    { 
                        ignoreUpdateInfo = true; 
                    }
 
                    // Add visuals for all lines.
                    if (textDetails.u.full.cLines > 0)
                    {
                        if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                        {
                            // (a) full with simple lines 
                            RenderSimpleLines(lineContainerVisual, ref textDetails.u.full, ignoreUpdateInfo); 
                        }
                        else 
                        {
                            // (b) full with composite lines - when figures/floaters are present
                            RenderCompositeLines(lineContainerVisual, ref textDetails.u.full, ignoreUpdateInfo);
                        } 
                    }
                    else 
                    { 
                        lineContainerVisual.Children.Clear();
                    } 
                }


                // Add visuals for floaters and figures. 
                if (textDetails.u.full.cAttachedObjects > 0)
                { 
                    ValidateVisualFloatersAndFigures(fskupdInherited, textDetails.u.full.cAttachedObjects); 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            // Mirror lines around the page.
            if(ThisFlowDirection != PageFlowDirection) 
            {
                PTS.FSRECT pageRect = _pageContext.PageRect;
                PtsHelper.UpdateMirroringTransform(PageFlowDirection, ThisFlowDirection, lineContainerVisual, TextDpi.FromTextDpi(2 * pageRect.u + pageRect.du));
            } 

        } 
 
        // -----------------------------------------------------------------
        // Updates viewport 
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, PtsHelper.FloaterListFromParagraph
        ///            PtsHelper.FigureListFromParagraph and UpdateViewportSimpleLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to UpdateViewportSimpleLines, PtsHelper.FloaterListFromParagraph, 
        ///        PtsHelper.FigureListFromParagraph and IsDeferredVisualCreationSupported is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateViewport(ref PTS.FSRECT viewport)
        {
            // Here's where the magic happens. 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected."); 

            if (IsDeferredVisualCreationSupported(ref textDetails.u.full)) 
            {
                // Query paragraph details and render its content
                ContainerVisual lineContainerVisual = _visual;
 
                Debug.Assert(!((TextParagraph) Paragraph).HasFiguresFloatersOrInlineObjects());
 
                UpdateViewportSimpleLines(lineContainerVisual, ref textDetails.u.full, ref viewport); 
            }
 
            int attachedObjectCount = textDetails.u.full.cAttachedObjects;

            // Recurse into figures and floaters
            if (attachedObjectCount > 0) 
            {
                // Get list of attached objects 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, attachedObjectCount, out arrayAttachedObjectDesc);
 
                // Arrange attached objects
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 

                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient); 

                    paraClient.UpdateViewport(ref viewport); 
                }
            }
        }
 
        // -----------------------------------------------------------------
        // Hit tests to the correct IInputElement within the paragraph 
        // that the mouse is over. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails,
        ///            PTS.FsQueryFloaterDetails, InputHitTestSimpleLines,
        ///            InputHitTestCompositeLines and some PtsHelper functions.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override IInputElement InputHitTest(PTS.FSPOINT pt) 
        {
            IInputElement ie = null;

            // Query paragraph details and hittest its content 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                PTS.FSPOINT localPoint = pt;
 
                // Mirror input point around page to hit test lines. 
                if(ThisFlowDirection != PageFlowDirection)
                { 
                    localPoint.u = _pageContext.PageRect.du - localPoint.u;
                }

                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    { 
                        // (a) full with simple lines
                        ie = InputHitTestSimpleLines(localPoint, ref textDetails.u.full); 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        ie = InputHitTestCompositeLines(localPoint, ref textDetails.u.full);
                    } 
                } 
                // Attached object hit tests are handled at page context level, as they're logically floating elements.
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            // If nothing is hit, return the owner of the paragraph.
            if (ie == null) 
            {
                ie = Paragraph.Element as IInputElement;
            }
 
            return ie;
        } 
 
        // -----------------------------------------------------------------
        // Returns ArrayList of rectangles for the given ContentElement 
        // if it is found. Returns empty list otherwise.
        // start: int representing start position for e relative to base TextContainer.
        // length: int representing number of positions occupied by e.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical, because calls Critical functions PTS.FsQueryTextDetails, 
        ///     PTS.FsQueryFloaterDetails, PTS.FsTransformRectangle, 
        ///     GetRectanglesInSimpleLines, GetRectanglesInCompositeLines and
        ///     some PtsHelper functions. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override List GetRectangles(ContentElement e, int start, int length) 
        { 
            List rectangles = new List();
            Debug.Assert(Paragraph.Element as ContentElement != e); 

            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                // Check figures and floaters
                if (textDetails.u.full.cAttachedObjects > 0)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION[] arrayAttachedObjectDesc;
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 
 
                    for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                    { 
                        PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];

                        BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                        PTS.ValidateHandle(paraClient); 

                        if (start < paraClient.Paragraph.ParagraphEndCharacterPosition) 
                        { 
                            rectangles = paraClient.GetRectangles(e, start, length);
                            Invariant.Assert(rectangles != null); 
                            if (rectangles.Count != 0)
                            {
                                break;
                            } 
                        }
                    } 
                } 

                // If no success with figures and floaters, check in line 
                if (rectangles.Count == 0 && textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        rectangles = GetRectanglesInSimpleLines(e, start, length, ref textDetails.u.full); 
                    } 
                    else
                    { 
                        // (b) full with complex lines
                        rectangles = GetRectanglesInCompositeLines(e, start, length, ref textDetails.u.full);
                    }
 
                    // Ensure these are specified in page coordinates.
                    if(rectangles.Count > 0 && ThisFlowDirection != PageFlowDirection) 
                    { 
                        PTS.FSRECT pageRect = _pageContext.PageRect;
 
                        for(int index = 0; index < rectangles.Count; index++)
                        {
                            PTS.FSRECT rectTransform = new PTS.FSRECT(rectangles[index]);
                            PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                            rectangles[index] = rectTransform.FromTextDpi();
                        } 
                    } 
                }
 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            Invariant.Assert(rectangles != null); 
            return rectangles;
        }

        // ------------------------------------------------------------------ 
        // Create paragraph result representing this paragraph.
        // ----------------------------------------------------------------- 
        internal override ParagraphResult CreateParagraphResult() 
        {
            return new TextParagraphResult(this); 
        }

        // ------------------------------------------------------------------
        // Returns a collection of LineResults for the paragraph. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails, 
        ///            LineResultsFromSimpleLines and LineResultsFromCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to LineResultsFromSimpleLines and
        ///        LineResultsFromCompositeLines is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetLineResults() 
        { 
#if TEXTPANELLAYOUTDEBUG
            TextPanelDebug.IncrementCounter("TextPara.GetLines", TextPanelDebug.Category.TextView); 
#endif
            ReadOnlyCollection lines = new ReadOnlyCollection(new List(0));

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0)
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines 
                        lines = LineResultsFromSimpleLines(ref textDetails.u.full);
                    }
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        lines = LineResultsFromCompositeLines(ref textDetails.u.full); 
                    } 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
 
            return lines;
        } 

        // -----------------------------------------------------------------
        // Returns a collection of UIElements representing floated objects.
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails and 
        ///            PtsHelper.FloaterListFromParagraph. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetFloaters() 
        {
            List floaters = null; 
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // Floaters are only supported by full paragraphs
            if (   textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull 
                && textDetails.u.full.cAttachedObjects > 0)
            { 
                // Get list of floaters 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 

                floaters = new List(arrayAttachedObjectDesc.Length);

                // Create view results for floaters 
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 

                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient);

                    if(paraClient is FloaterParaClient)
                    { 
                        floaters.Add(paraClient.CreateParagraphResult());
                    } 
                } 
            }
            return (floaters != null && floaters.Count > 0) ? new ReadOnlyCollection(floaters) : null; 
        }

        // ------------------------------------------------------------------
        // Returns a collection of UIElements representing positioned objects. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetFigures() 
        {
            List figures = null; 
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // Floaters are only supported by full paragraphs
            if (   textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull 
                && textDetails.u.full.cAttachedObjects > 0)
            { 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc);
 
                figures = new List(arrayAttachedObjectDesc.Length);

                // Create view results for figures
                for (int index = 0; index < arrayAttachedObjectDesc.Length; index++) 
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; 
 
                    BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                    PTS.ValidateHandle(paraClient); 

                    if(paraClient is FigureParaClient)
                    {
                        figures.Add(paraClient.CreateParagraphResult()); 
                    }
                } 
            } 
            return (figures != null && figures.Count > 0) ? new ReadOnlyCollection(figures) : null;
        } 

        // ------------------------------------------------------------------
        // Return TextContentRange for the content of the paragraph.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override TextContentRange GetTextContentRange()
        { 
            PTS.FSTEXTDETAILS textDetails;
            int dcpFirst = 0, dcpLast = 0; 
 
            // Query paragraph details
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");

            dcpFirst = textDetails.u.full.dcpFirst; 
            dcpLast = textDetails.u.full.dcpLim;
 
            // The last TextParaClient has EOP character included, which does not 
            // exist in the tree. Need to remove it.
            // NOTE: cannot remove it when formatting line because PTS does not like empty lines. 
            if (HasEOP && dcpLast > Paragraph.Cch)
            {
                ErrorHandler.Assert(dcpLast == Paragraph.Cch + Line.SyntheticCharacterLength, ErrorHandler.ParagraphCharacterCountMismatch);
                dcpLast -= Line.SyntheticCharacterLength; 
            }
 
            // Text paragraph has always just one range 
            int dcp = Paragraph.ParagraphStartCharacterPosition;
            TextContentRange textContentRange; 

            if(TextParagraph.HasFiguresOrFloaters())
            {
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc = null; 

                int attachedObjectCount = textDetails.u.full.cAttachedObjects; 
                textContentRange = new TextContentRange(); 

                // Recurse into figures and floaters 
                if (attachedObjectCount > 0)
                {
                    // Get list of attached objects
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, attachedObjectCount, out arrayAttachedObjectDesc); 
                }
 
                // Figures and floaters cannot break 
                TextParagraph.UpdateTextContentRangeFromAttachedObjects(textContentRange, dcp + dcpFirst, dcp + dcpLast, arrayAttachedObjectDesc);
 
            }
            else
            {
                textContentRange = new TextContentRange(dcp + dcpFirst, dcp + dcpLast, Paragraph.StructuralCache.TextContainer); 
            }
 
            return textContentRange; 
        }
 
        // -----------------------------------------------------------------
        // Retrieves detailed information about a line of text.
        //
        //     dcpLine - Index of the first character in the line. 
        //     cchContent - Number of content characters in the line.
        //     cchEllipses - Number of content characters hidden by ellipses. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, 
        ///            PtsHelper.LineListSimpleFromTextPara and PtsHelper.LineListCompositeFromTextPara.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void GetLineDetails(int dcpLine, out int cchContent, out int cchEllipses) 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            int lineWidth = 0; 
            bool firstLine = (dcpLine == 0);
            int dcpLim = 0; 
            IntPtr breakRecLine = IntPtr.Zero; 

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc);
 
                        // Get lines information
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                            if (dcpLine == lineDesc.dcpFirst)
                            {
                                lineWidth = lineDesc.dur;
 
                                // Store dcpLim to check that line lengths are in [....]
                                dcpLim = lineDesc.dcpLim; 
 
                                breakRecLine = lineDesc.pfsbreakreclineclient;
 
                                break;
                            }
                        }
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present 
                        PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        // Get lines information
                        int index;
                        for (index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                            if (lineDesc.cElements == 0) continue; 

                            // Get list of line elements. 
                            PTS.FSLINEELEMENT[] arrayLineElement;
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);

                            int elIndex; 
                            for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                            { 
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                                if (element.dcpFirst == dcpLine) 
                                {
                                    lineWidth = element.dur;

                                    // Store dcpLim to check that line lengths are in [....] 
                                    dcpLim = element.dcpLim;
 
                                    breakRecLine = element.pfsbreakreclineclient; 
                                    break;
                                } 
                            }
                            if (elIndex < arrayLineElement.Length)
                            {
                                firstLine = (index == 0); 
                                break;
                            } 
                        } 
                    }
                } 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            // Recreate text line 
            Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache);
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);

            if(IsOptimalParagraph) 
            {
                ctx.LineFormatLengthTarget = dcpLim - dcpLine; 
            } 

            TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); 

            // Assert that number of characters in Text line is the same as our expected length
            Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]");
 
            cchContent = line.ContentLength;
            cchEllipses = line.GetEllipsesLength(); 
 
            line.Dispose();
        } 

        // -----------------------------------------------------------------
        // Retrieves baseline information for first line of text
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails, 
        ///            PtsHelper.LineListSimpleFromTextPara and PtsHelper.LineListCompositeFromTextPara. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override int GetFirstTextLineBaseline() 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");

            Rect rect = System.Windows.Rect.Empty;
            int vrBaseline = 0; 

            if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
            { 
                // (a) full with simple lines
                RectFromDcpSimpleLines(0, 0, LogicalDirection.Forward, TextPointerContext.Text, ref textDetails.u.full, ref rect, ref vrBaseline); 
            }
            else
            {
                // (b) full with composite lines - when figures/floaters are present 
                RectFromDcpCompositeLines(0, 0, LogicalDirection.Forward, TextPointerContext.Text, ref textDetails.u.full, ref rect, ref vrBaseline);
            } 
 
            return vrBaseline;
        } 


        // -----------------------------------------------------------------
        // Retrieves ITextPosition for specified character position. 
        //
        //      dcp - Offset from the beginning of the text paragraph. 
        // ------------------------------------------------------------------ 
        internal ITextPointer GetTextPosition(int dcp, LogicalDirection direction)
        { 
            return TextContainerHelper.GetTextPointerFromCP(Paragraph.StructuralCache.TextContainer, dcp + Paragraph.ParagraphStartCharacterPosition, direction);
        }

        // ----------------------------------------------------------------- 
        // Retrieves bounds of an object/character at the specified ITextPointer.
        // 
        //      position - Position of an object/character. 
        //
        // Returns: Bounds of an object/character. 
        // ------------------------------------------------------------------
        /// 
        /// Critical, because calls the Critical functions PTS.FsQueryTextDetails,
        ///     PTS.FsTransformRectangle, RectFromDcpSimpleLines and 
        ///     RectFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to RectFromDcpSimpleLines and
        ///        RectFromDcpCompositeLines is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Rect GetRectangleFromTextPosition(ITextPointer position)
        { 
            Rect rect = System.Windows.Rect.Empty;
 
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)position); 
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition;
            int originalDcp = dcp; 
            if (position.LogicalDirection == LogicalDirection.Backward && dcp > 0)
            {
                --dcp;
            } 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0) 
                {
                    int vrBaseline = 0; 

                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    {
                        // (a) full with simple lines 
                        RectFromDcpSimpleLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline);
                    } 
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        RectFromDcpCompositeLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline);
                    }
                }
            } 
            else
            { 
                // (c) cached - when using ParaChache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }

            // Mirror back to page flow direction
            if(ThisFlowDirection != PageFlowDirection) 
            {
                PTS.FSRECT pageRect = _pageContext.PageRect; 
                PTS.FSRECT rectTransform = new PTS.FSRECT(rect); 
                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform));
                rect = rectTransform.FromTextDpi(); 
            }

            return rect;
        } 

        // ------------------------------------------------------------------ 
        // Returns tight bounding path geometry. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            RectFromDcpSimpleLines and RectFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to RectFromDcpSimpleLines and
        ///        RectFromDcpCompositeLines is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, double paragraphTopSpace, Rect visibleRect) 
        {
            Geometry geometry = null;
            Geometry floatAndFigGeometry = null;
 
            int cpStartTextPointer = startPosition.Offset;
            int cpParagraphStart = Paragraph.ParagraphStartCharacterPosition; 
            int dcpStart = Math.Max(cpStartTextPointer, cpParagraphStart) - cpParagraphStart; 

            int cpEndTextPointer = endPosition.Offset; 
            int cpParagraphEnd = Paragraph.ParagraphEndCharacterPosition;
            int dcpEnd = Math.Min(cpEndTextPointer, cpParagraphEnd) - cpParagraphStart;

            //  apply first line top space only if selection starts before or exactly at this paragraph 
            double firstLineTopSpace = (cpStartTextPointer < cpParagraphStart) ? paragraphTopSpace : 0.0;
 
            //  handle end-of-para only if the range extends beyond this paragraph 
            bool handleEndOfPara = cpEndTextPointer > cpParagraphEnd;
 
            //  mirror transform - needed if flow direction changes
            Transform transform = null;

            if (ThisFlowDirection != PageFlowDirection) 
            {
                transform = new MatrixTransform(-1.0, 0.0, 0.0, 1.0, TextDpi.FromTextDpi(2 * _pageContext.PageRect.u + _pageContext.PageRect.du), 0.0); 
 
                //  (and while we are at it) visibleRect should be mirrored too
                visibleRect = transform.TransformBounds(visibleRect); 
            }

            //  query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaCache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            {
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    { 
                        // (a) full with simple lines
                        geometry = PathGeometryFromDcpRangeSimpleLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect); 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        geometry = PathGeometryFromDcpRangeCompositeLines(dcpStart, dcpEnd, firstLineTopSpace, handleEndOfPara, ref textDetails.u.full, visibleRect);
                    } 
                } 
                //  build highlight for floaters and figures in this paragraph
                if (textDetails.u.full.cAttachedObjects > 0) 
                {
                    floatAndFigGeometry = PathGeometryFromDcpRangeFloatersAndFigures(cpStartTextPointer, cpEndTextPointer, ref textDetails.u.full);
                }
            } 
            else
            { 
                // (c) cached - when using ParaCache 
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }

            //  at this point geometry contains only the text content related geometry
            if (geometry != null && transform != null) 
            {
                //  mirror back to page flow direction 
                CaretElement.AddTransformToGeometry(geometry, transform); 
            }
 
            //  rectangles from which floatAndFigGeometry is calculated are already mirrored.
            //  this is why geometry and floatAndFigGeometry are combined after geometry is mirrored above
            if (floatAndFigGeometry != null)
            { 
                CaretElement.AddGeometry(ref geometry, floatAndFigGeometry);
            } 
 
            return (geometry);
        } 

        // ------------------------------------------------------------------
        // Returns true if caret is at unit boundary
        // 
        //      position - Position of an object/character.
        // 
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails, 
        ///            IsAtCaretUnitBoundaryFromDcpSimpleLines and
        ///            IsAtCaretUnitBoundaryFromDcpCompositeLines.
        /// Safe - The IntPtr parameter passed to PTS.FsQueryTextDetails is SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to IsAtCaretUnitBoundaryFromDcpSimpleLines and
        ///        IsAtCaretUnitBoundaryFromDcpCompositeLines is generated securely in this function. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal bool IsAtCaretUnitBoundary(ITextPointer position) 
        {
            bool isAtCaretUnitBoundary = false;

            // Get position offset in paragraph 
            Debug.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer); 
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition; 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        isAtCaretUnitBoundary = IsAtCaretUnitBoundaryFromDcpSimpleLines(dcp, position, ref textDetails.u.full); 
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        isAtCaretUnitBoundary = IsAtCaretUnitBoundaryFromDcpCompositeLines(dcp, position, ref textDetails.u.full);
                    }
                } 
            }
            else 
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            }

            return isAtCaretUnitBoundary; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns next caret unit position
        // 
        //      position - Position of an object/character.
        //      direction - Logical direction in which we seek the position
        //
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails, 
        ///            NextCaretUnitPositionFromDcpSimpleLines and 
        ///            NextCaretUnitPositionFromDcpCompositeLines.
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet 
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to the other two Critical functions is
        ///        generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction) 
        { 
            ITextPointer nextCaretPosition = position;
 
            // Get position offset in paragraph
            Debug.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer);
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition; 

            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
 
            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines
                        nextCaretPosition = NextCaretUnitPositionFromDcpSimpleLines(dcp, position, direction, ref textDetails.u.full);
                    } 
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present 
                        nextCaretPosition = NextCaretUnitPositionFromDcpCompositeLines(dcp, position, direction, ref textDetails.u.full);
                    } 
                }
            }
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Debug.Assert(false, "Should not get here. ParaCache is not currently used."); 
            }
 
            return nextCaretPosition;
        }

        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            BackspaceCaretUnitPositionFromDcpSimpleLines and 
        ///            BackspaceCaretUnitPositionFromDcpCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to BackspaceCaretUnitPositionFromDcpSimpleLines and
        ///        BackspaceCaretUnitPositionFromDcpCompositeLines is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position)
        { 
            ITextPointer backspaceCaretPosition = position; 

            // Get position offset in paragraph 
            Invariant.Assert(position is TextPointer);
            int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition(position as TextPointer);
            int dcp = cp - Paragraph.ParagraphStartCharacterPosition;
 
            // Query paragraph details
            PTS.FSTEXTDETAILS textDetails; 
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        backspaceCaretPosition = BackspaceCaretUnitPositionFromDcpSimpleLines(dcp, position, ref textDetails.u.full);
                    }
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        backspaceCaretPosition = BackspaceCaretUnitPositionFromDcpCompositeLines(dcp, position, ref textDetails.u.full); 
                    }
                } 
            }
            else
            {
                // (c) cached - when using ParaChache 
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used."); 
            } 

            return backspaceCaretPosition; 
        }

        // ------------------------------------------------------------------
        // Retrieves a text position given the distance from the beginning 
        // of the line.
        // 
        //      dcpLine - Character offset identifying the line. This is 
        //          the first character position of the line.
        //      distance - Distance from the beginning of the line. 
        //
        // Returns: Text position.
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails,
        ///            LineListSimpleFromTextPara, LineListCompositeFromTextPara and 
        ///            LineElementListFromCompositeLine. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ITextPointer GetTextPositionFromDistance(int dcpLine, double distance) 
        {
            // Query paragraph details 
            int urDistance = TextDpi.ToTextDpi(distance); 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            if(ThisFlowDirection != PageFlowDirection)
            {
                urDistance = _pageContext.PageRect.du - urDistance; 
            }
 
            int lineWidth = 0; 
            bool firstLine = (dcpLine == 0);
            int dcpLim = 0; 
            IntPtr breakRecLine = IntPtr.Zero;

            // There are 3 different types of text paragraphs:
            // (a) full with simple lines 
            // (b) full with composite lines - when figures/floaters are present
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            {
                if (textDetails.u.full.cLines > 0) 
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    {
                        // (a) full with simple lines 
                        PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 
 
                        // Get lines information
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
                            if (dcpLine == lineDesc.dcpFirst) 
                            {
                                lineWidth = lineDesc.dur; 
                                urDistance -= lineDesc.urStart; 

                                // Store dcpLim to check if line lengths are in [....] 
                                dcpLim = lineDesc.dcpLim;

                                breakRecLine = lineDesc.pfsbreakreclineclient;
 
                                break;
                            } 
                        } 
                    }
                    else 
                    {
                        // (b) full with composite lines - when figures/floaters are present
                        PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        // Get lines information 
                        int index; 
                        for (index = 0; index < arrayLineDesc.Length; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                            if (lineDesc.cElements == 0) continue;

                            // Get list of line elements. 
                            PTS.FSLINEELEMENT [] arrayLineElement;
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                            int elIndex;
                            for (elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                            {
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                                if (element.dcpFirst == dcpLine) 
                                {
                                    lineWidth = element.dur; 
                                    urDistance -= element.urStart; 

                                    // Store dcpLim to check if line lengths are in [....] 
                                    dcpLim = element.dcpLim;

                                    breakRecLine = element.pfsbreakreclineclient;
 
                                    break;
                                } 
                            } 
                            if (elIndex < arrayLineElement.Length)
                            { 
                                firstLine = (index == 0);
                                break;
                            }
                        } 
                    }
                } 
            } 
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 

            // Recreate text line 
            Line.FormattingContext ctx = new Line.FormattingContext(false, true, true, TextParagraph.TextRunCache); 
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
 
            if(IsOptimalParagraph)
            {
                ctx.LineFormatLengthTarget = dcpLim - dcpLine;
            } 

            TextParagraph.FormatLineCore(line, breakRecLine, ctx, dcpLine, lineWidth, firstLine, dcpLine); 
 
            // Assert that number of characters in Text line is the same as our expected length
            Invariant.Assert(line.SafeLength == dcpLim - dcpLine, "Line length is out of [....]"); 

            CharacterHit charHit = line.GetTextPositionFromDistance(urDistance);
            int cpPosition = charHit.FirstCharacterIndex + charHit.TrailingLength;
            int dcpLastAttachedObject = TextParagraph.GetLastDcpAttachedObjectBeforeLine(dcpLine); 
            if(cpPosition < dcpLastAttachedObject)
            { 
                cpPosition = dcpLastAttachedObject; 
            }
 
            StaticTextPointer pos = TextContainerHelper.GetStaticTextPointerFromCP(Paragraph.StructuralCache.TextContainer, cpPosition + Paragraph.ParagraphStartCharacterPosition);
            LogicalDirection logicalDirection = (charHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
            line.Dispose();
 
            return pos.CreateDynamicTextPointer(logicalDirection);
        } 
 
        // ------------------------------------------------------------------
        // Retrieves collection of GlyphRuns from a range of text. 
        //
        //      glyphRuns - preallocated collection of GlyphRuns. May already
        //          contain runs and new runs need to be appended.
        //      start - the beginning of the range 
        //      end - the end of the range
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls the Critical functions PTS.FsQueryTextDetails,
        ///            GetGlyphRunsFromSimpleLines and GetGlyphRunsFromCompositeLines. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to GetGlyphRunsFromSimpleLines and
        ///        GetGlyphRunsFromCompositeLines is generated securely in this function. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void GetGlyphRuns(List glyphRuns, ITextPointer start, ITextPointer end) 
        {
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));

            // There are 3 different types of text paragraphs: 
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache 
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull)
            { 
                int dcpStart = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)start) - Paragraph.ParagraphStartCharacterPosition;
                int dcpEnd = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)end) - Paragraph.ParagraphStartCharacterPosition;
                Invariant.Assert(dcpStart >= textDetails.u.full.dcpFirst && dcpEnd <= textDetails.u.full.dcpLim);
 
                if (textDetails.u.full.cLines > 0)
                { 
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) 
                    {
                        // (a) full with simple lines 
                        GetGlyphRunsFromSimpleLines(glyphRuns, dcpStart, dcpEnd, ref textDetails.u.full);
                    }
                    else
                    { 
                        // (b) full with composite lines - when figures/floaters are present
                        GetGlyphRunsFromCompositeLines(glyphRuns, dcpStart, dcpEnd, ref textDetails.u.full); 
                    } 
                }
            } 
            else
            {
                // (c) cached - when using ParaChache
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); 
                Invariant.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
        } 

        #endregion Internal Methods 

        // -----------------------------------------------------------------
        //
        //  Internal Properties 
        //
        // ------------------------------------------------------------------ 
 
        #region Internal Properties
 
        // -----------------------------------------------------------------
        // Paragraph associated with this ParaClient.
        // -----------------------------------------------------------------
        internal TextParagraph TextParagraph { get { return (TextParagraph)_paragraph; } } 

        // ----------------------------------------------------------------- 
        // Has EOP character? Is it the last ParaClient of TextParagraph? 
        // ------------------------------------------------------------------
        internal bool HasEOP 
        {
            get { return IsLastChunk; }
        }
 
        // -----------------------------------------------------------------
        // Is this the first chunk of paginated content. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        /// 
        internal override bool IsFirstChunk 
        {
            [SecurityCritical, SecurityTreatAsSafe] 
            get 
            {
                // Query paragraph details 
                PTS.FSTEXTDETAILS textDetails;
                PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected.");
                // The first chunk always starts with dcpFirst == 0. 
                return (textDetails.u.full.cLines > 0 && textDetails.u.full.dcpFirst == 0);
            } 
        } 

        // ------------------------------------------------------------------ 
        // Is this the last chunk of paginated content.
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTextDetails. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///  
        internal override bool IsLastChunk
        { 
            [SecurityCritical, SecurityTreatAsSafe]
            get
            {
                bool lastChunk = false; 

                // Query paragraph details 
                PTS.FSTEXTDETAILS textDetails; 
                PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails));
                Invariant.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull, "Only 'full' text paragraph type is expected."); 

                if (textDetails.u.full.cLines > 0)
                {
                    if (Paragraph.Cch > 0) 
                    {
                        lastChunk = (textDetails.u.full.dcpLim >= Paragraph.Cch); 
                    } 
                    else
                    { 
                        lastChunk = (textDetails.u.full.dcpLim == Line.SyntheticCharacterLength);
                        //

 
                    }
                } 
                return lastChunk; 
            }
        } 

        #endregion Internal Properties

        // ------------------------------------------------------------------ 
        //
        //  Protected Methods 
        // 
        // -----------------------------------------------------------------
 
        #region Protected Methods

        // -----------------------------------------------------------------
        // Arrange paragraph. 
        // -----------------------------------------------------------------
        ///  
        /// Critical, because calls Critical functions PTS.FsQueryTextDetails, 
        ///     PTS.FsQueryFloaterDetails, PTS.FsTransformRectangle and
        ///     some functions on PtsHelper. 
        /// Safe - The IntPtr parameters passed to PTS.FsQueryTextDetails are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value.
        ///        The textdetails parameter passed to other methods is generated securely in this function.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnArrange() 
        { 
            base.OnArrange();
 
            // Optimization - Don't arrange if we have no figures, floaters, inline objects
            if(!TextParagraph.HasFiguresFloatersOrInlineObjects())
            {
                return; 
            }
 
            // Query paragraph details 
            PTS.FSTEXTDETAILS textDetails;
            PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); 

            // There are 3 different types of text paragraphs:
            // (a) full with simple lines
            // (b) full with composite lines - when figures/floaters are present 
            // (c) cached - when using ParaChache
            if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) 
            { 
                // (a) full with simple lines
                // (b) full with composite lines - when figures/floaters are present 
                if (textDetails.u.full.cLines > 0)
                {
                    if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite))
                    { 
                        // (a) full with simple lines
                        PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
                        PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 

                        for (int index = 0; index < arrayLineDesc.Length; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                            // Enumerate all inline objects and reformat them. 
                            List inlineObjects = TextParagraph.InlineObjectsFromRange(lineDesc.dcpFirst, lineDesc.dcpLim);
                            if (inlineObjects != null) 
                            { 
                                for (int i = 0; i < inlineObjects.Count; i++)
                                { 
                                    UIElement uiElement = (UIElement)inlineObjects[i].Element;

                                    if(uiElement.IsMeasureValid && !uiElement.IsArrangeValid)
                                    { 
                                        uiElement.Arrange(new Rect(uiElement.DesiredSize));
                                    } 
                                } 
                            }
                        } 
                    }
                    else
                    {
                        // (b) full with composite lines - when figures/floaters are present 
                        PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
                        PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails.u.full, out arrayLineDesc); 
 
                        for (int index = 0; index < arrayLineDesc.Length; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];

                            // Get list of line elements
                            PTS.FSLINEELEMENT[] arrayLineElement; 
                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                            for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                            {
                                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                                // Enumerate all inline objects and reformat them.
                                List inlineObjects = TextParagraph.InlineObjectsFromRange(element.dcpFirst, element.dcpLim);
                                if (inlineObjects != null) 
                                {
                                    for (int i = 0; i < inlineObjects.Count; i++) 
                                    { 
                                        UIElement uiElement = (UIElement)inlineObjects[i].Element;
                                        if(uiElement.IsMeasureValid && !uiElement.IsArrangeValid) 
                                        {
                                            uiElement.Arrange(new Rect(uiElement.DesiredSize));
                                        }
                                    } 
                                }
                            } 
                        } 
                    }
                } 

                if (textDetails.u.full.cAttachedObjects > 0)
                {
                    // Get list of floaters 
                    PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                    PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); 
 
                    // Arrange floaters
 
                    for (int index = 0; index < arrayAttachedObjectDesc.Length; index++)
                    {
                        PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];
 
                        BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient;
                        PTS.ValidateHandle(paraClient); 
 
                        if(paraClient is FloaterParaClient)
                        { 
                            PTS.FSFLOATERDETAILS floaterDetails;
                            PTS.Validate(PTS.FsQueryFloaterDetails(PtsContext.Context, attachedObjectDesc.pfspara, out floaterDetails));
                            PTS.FSRECT rectFloater = floaterDetails.fsrcFloater;
 
                            if(ThisFlowDirection != PageFlowDirection)
                            { 
                                PTS.FSRECT pageRect = _pageContext.PageRect; 
                                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectFloater, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectFloater));
                            } 

                            ((FloaterParaClient)paraClient).ArrangeFloater(rectFloater, _rect, PTS.FlowDirectionToFswdir(ThisFlowDirection), _pageContext);
                        }
                        else if(paraClient is FigureParaClient) 
                        {
                            PTS.FSFIGUREDETAILS figureDetails; 
                            PTS.Validate(PTS.FsQueryFigureObjectDetails(PtsContext.Context, attachedObjectDesc.pfspara, out figureDetails)); 
                            PTS.FSRECT rectFigure = figureDetails.fsrcFlowAround;
 
                            if(ThisFlowDirection != PageFlowDirection)
                            {
                                PTS.FSRECT pageRect = _pageContext.PageRect;
                                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectFigure, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectFigure)); 
                            }
 
                            ((FigureParaClient)paraClient).ArrangeFigure(rectFigure, _rect, PTS.FlowDirectionToFswdir(ThisFlowDirection), _pageContext); 
                        }
                        else 
                        {
                            Invariant.Assert(false, "Attached object not figure or floater.");
                        }
 
                    }
                } 
            } 
            else
            { 
                // (c) cached - when using ParaChache
                Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached);
                Debug.Assert(false, "Should not get here. ParaCache is not currently used.");
            } 
        }
 
        #endregion Protected Methods 

        // ------------------------------------------------------------------ 
        //
        //  Private Methods
        //
        // ----------------------------------------------------------------- 

        #region Private Methods 
 
        // ------------------------------------------------------------------
        // Syncs a deferred line visuals list (and update information) with existing visuals 
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical function PtsHelper.LineListSimpleFromTextPara
        ///  
        [SecurityCritical]
        private void SyncUpdateDeferredLineVisuals(VisualCollection lineVisuals, ref PTS.FSTEXTDETAILSFULL textDetails, bool ignoreUpdateInfo) 
        { 
            Debug.Assert(!PTS.ToBoolean(textDetails.fLinesComposite));
 
            try
            {
                if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo)
                { 
                    // _lineIndexFirstVisual will be updated based on the size of this list, so clearing is sufficient here.
                    lineVisuals.Clear(); 
                } 
                else if (_lineIndexFirstVisual != -1)
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
                    PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

                    int lineIndexToBeginRemoval = textDetails.cLinesBeforeChange; 
                    int cLinesToRemove = textDetails.cLinesChanged - textDetails.dcLinesChanged;
                    int insertionIndex = -1; 
 
                    // Shift lines before change
                    if(textDetails.dvrShiftBeforeChange != 0) 
                    {
                        int countVisualsShiftBeforeChange = Math.Min(Math.Max(lineIndexToBeginRemoval - _lineIndexFirstVisual, 0), lineVisuals.Count);
                        for(int index = 0; index < countVisualsShiftBeforeChange; index++)
                        { 
                            // Shift line's visual
                            ContainerVisual lineVisual = (ContainerVisual) lineVisuals[index]; 
                            Vector offset = lineVisual.Offset; 
                            offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange);
                            lineVisual.Offset = offset; 
                        }
                    }

                    // If the line index to begin removal is before our first visual, then the overlap will look like 
                    //      |---------------|  (Committed visual range)
                    // |------|                (Range to remove) 
                    if (lineIndexToBeginRemoval < _lineIndexFirstVisual) 
                    {
                        // Determine the amount of overlap, and remove. 
                        int actualLinesToRemove = Math.Min(Math.Max(lineIndexToBeginRemoval - _lineIndexFirstVisual + cLinesToRemove, 0), lineVisuals.Count);

                        if (actualLinesToRemove > 0)
                        { 
                            lineVisuals.RemoveRange(0, actualLinesToRemove);
                        } 
 
                        if (lineVisuals.Count == 0)
                        { 
                            lineVisuals.Clear();
                            _lineIndexFirstVisual = -1;
                        }
                        else 
                        {
                            insertionIndex = 0; 
                            _lineIndexFirstVisual = lineIndexToBeginRemoval; 
                        }
                    } 
                    else if (lineIndexToBeginRemoval < _lineIndexFirstVisual + lineVisuals.Count)
                    {
                        // Else case for overlap
                        //  |---------------|  (Committed visual range) 
                        //       |-----|                (Range to remove)
                        // Or 
                        //  |---------------| 
                        //           |--------------|
 
                        // Removing from the middle
                        int actualLinesToRemove = Math.Min(cLinesToRemove, lineVisuals.Count - (lineIndexToBeginRemoval - _lineIndexFirstVisual));

                        lineVisuals.RemoveRange(lineIndexToBeginRemoval - _lineIndexFirstVisual, actualLinesToRemove); 

                        insertionIndex = lineIndexToBeginRemoval - _lineIndexFirstVisual; // Insertion index is relative to committed visual range 
                    } 

                    int shiftIndex = -1; 

                    if (insertionIndex != -1)
                    {
                        // Add new lines 
                        // Insertion must occur at some point along our committed visual range
                        Debug.Assert(insertionIndex >= 0 && insertionIndex <= lineVisuals.Count); 
 
                        for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++)
                        { 
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                            ContainerVisual lineVisual = CreateLineVisual(ref arrayLineDesc[index], Paragraph.ParagraphStartCharacterPosition);
 
                            lineVisuals.Insert(insertionIndex + (index - textDetails.cLinesBeforeChange), lineVisual);
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        } 

                        shiftIndex = insertionIndex + textDetails.cLinesChanged; 
                    }

                    // Any committed visuals after our inserted section must be shifted
                    if (shiftIndex != -1) 
                    {
                        // Shift remaining lines 
                        for (int index = shiftIndex; index < lineVisuals.Count; index++) 
                        {
                            // Shift line's visual 
                            ContainerVisual lineVisual = (ContainerVisual) lineVisuals[index];
                            Vector offset = lineVisual.Offset;
                            offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange);
                            lineVisual.Offset = offset; 
                        }
                    } 
                } 
            }
 
            finally
            {
                // If no visuals, committed range is nonexistant, so -1
                if (lineVisuals.Count == 0) 
                {
                    _lineIndexFirstVisual = -1; 
                } 
            }
 
#if VERIFY_VISUALS
            // Verify our visuals are in-[....] with the actual line visuals.
            VerifyVisuals(ref textDetails);
#endif 
        }
 
        // ----------------------------------------------------------------- 
        // Retrieve lines from simple lines.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical, because:
        ///     a) calls Critical function PtsHelper.LineListSimpleFromTextPara
        ///     b) calls Critical function PTS.FsTransformRectangle 
        /// 
        [SecurityCritical] 
        private ReadOnlyCollection LineResultsFromSimpleLines(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            List lines = new List(arrayLineDesc.Length); 
 
            // Get lines information
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                Rect lbox = new Rect( 
                    TextDpi.FromTextDpi(lineDesc.urBBox), TextDpi.FromTextDpi(lineDesc.vrStart),
                    TextDpi.FromTextDpi(lineDesc.durBBox), TextDpi.FromTextDpi(lineDesc.dvrAscent + lineDesc.dvrDescent)); 
 
                // Mirror layout box to page flow direction
                if(PageFlowDirection != ThisFlowDirection) 
                {
                    PTS.FSRECT pageRect = _pageContext.PageRect;
                    PTS.FSRECT rectTransform = new PTS.FSRECT(lbox);
                    PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                    lbox = rectTransform.FromTextDpi();
                } 
 
                lines.Add(new TextParaLineResult(this, lineDesc.dcpFirst, lineDesc.dcpLim - lineDesc.dcpFirst,
                    lbox, TextDpi.FromTextDpi(lineDesc.dvrAscent))); 
            }

            if (lines.Count != 0)
            { 
                // Hide EOP character
                TextParaLineResult lastLineResult = (TextParaLineResult)lines[lines.Count - 1]; 
                if (HasEOP && lastLineResult.DcpLast > Paragraph.Cch) 
                {
                    ErrorHandler.Assert(lastLineResult.DcpLast - Line.SyntheticCharacterLength == Paragraph.Cch, ErrorHandler.ParagraphCharacterCountMismatch); 
                    lastLineResult.DcpLast -= Line.SyntheticCharacterLength;
                }
            }
 
            return (lines.Count > 0) ? new ReadOnlyCollection(lines) : null;
        } 
 
        // -----------------------------------------------------------------
        // Retrieve lines from composite lines. 
        // -----------------------------------------------------------------
        /// 
        /// Critical, because:
        ///     a) calls Critical function PtsHelper.LineListCompositeFromTextPara 
        ///     b) calls Critical function PtsHelper.LineElementListFromCompositeLine.
        ///     c) calls Critical function PTS.FsTransformRectangle 
        ///  
        [SecurityCritical]
        private ReadOnlyCollection LineResultsFromCompositeLines(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            // Get list of complex composite lines. 
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            List lines = new List(arrayLineDesc.Length);
 
            // Get lines information
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }
 
                // Get list of line elements. 
                PTS.FSLINEELEMENT [] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 

                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // Create line info 
                    Rect lbox = new Rect(TextDpi.FromTextDpi(element.urBBox), TextDpi.FromTextDpi(lineDesc.vrStart), 
                                         TextDpi.FromTextDpi(element.durBBox), TextDpi.FromTextDpi(element.dvrAscent + element.dvrDescent));
 
                    // Mirror layout box to page flow direction
                    if(ThisFlowDirection != PageFlowDirection)
                    {
                        PTS.FSRECT pageRect = _pageContext.PageRect; 
                        PTS.FSRECT rectTransform = new PTS.FSRECT(lbox);
                        PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); 
                        lbox = rectTransform.FromTextDpi(); 
                    }
 
                    lines.Add(new TextParaLineResult(this, element.dcpFirst, element.dcpLim - element.dcpFirst,
                        lbox, TextDpi.FromTextDpi(element.dvrAscent)));
                }
            } 

            if (lines.Count != 0) 
            { 
                // Hide EOP character
                TextParaLineResult lastLineResult = (TextParaLineResult)lines[lines.Count - 1]; 
                if (HasEOP && lastLineResult.DcpLast > Paragraph.Cch)
                {
                    ErrorHandler.Assert(lastLineResult.DcpLast - Line.SyntheticCharacterLength == Paragraph.Cch, ErrorHandler.ParagraphCharacterCountMismatch);
                    lastLineResult.DcpLast -= Line.SyntheticCharacterLength; 
                }
            } 
 
            return (lines.Count > 0) ? new ReadOnlyCollection(lines) : null;
        } 

        // -----------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position.
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara 
        ///  
        [SecurityCritical]
        private void RectFromDcpSimpleLines( 
            int dcp,
            int originalDcp,
            LogicalDirection orientation,
            TextPointerContext context, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            ref Rect rect, 
            ref int vrBaseline) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line.
                // But if this is the last line (EOP character), get rectangle form the last 
                // character of the line.
                if (   ((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) 
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);

                    if(IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Get rect from cp
                    FlowDirection flowDirection; 
                    rect = line.GetBoundsFromTextPosition(dcp, out flowDirection); 
                    rect.X += TextDpi.FromTextDpi(lineDesc.urStart);
                    rect.Y += TextDpi.FromTextDpi(lineDesc.vrStart); 

                    // Return only TopLeft and Height.
                    // Adjust rect.Left by taking into account flow direction of the
                    // content and orientation of input position. 
                    if (ThisFlowDirection != flowDirection)
                    { 
                        if (orientation == LogicalDirection.Forward) 
                        {
                            rect.X = rect.Right; 
                        }
                    }
                    else
                    { 
                        // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning
                        //       content with Backward orientation. This should not be a valid position. 
                        //       Remove it later 
                        // We also need to check here if the context is an inline element, such as a hidden run. In such a
                        // case we will have the rect of the character immediately following the hidden run, which is the same as 
                        // originalDcp. If we take the right bounds of the rect case we will be off by one character.
                        if (orientation == LogicalDirection.Backward && originalDcp > 0 && (context == TextPointerContext.Text || context == TextPointerContext.EmbeddedElement))
                        {
                            rect.X = rect.Right; 
                        }
                    } 
                    rect.Width = 0; 

                    vrBaseline = line.Baseline + lineDesc.vrStart; 

                    // Dispose the line
                    line.Dispose();
                    break; 
                }
            } 
        } 

        // ----------------------------------------------------------------- 
        // Retrieve bounds of an object/character at specified text position.
        // ------------------------------------------------------------------
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        ///  
        [SecurityCritical]
        private void RectFromDcpCompositeLines( 
            int dcp,
            int originalDcp,
            LogicalDirection orientation,
            TextPointerContext context, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            ref Rect rect, 
            ref int vrBaseline) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT [] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line.
                    // But if this is the last line (EOP character), get rectangle form the last 
                    // character of the line.
                    if (   ((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) 
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    {
                        // Create and format line 
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);

                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Get rect from cp
                        FlowDirection flowDirection; 
                        rect = line.GetBoundsFromTextPosition(dcp, out flowDirection); 
                        rect.X += TextDpi.FromTextDpi(element.urStart);
                        rect.Y += TextDpi.FromTextDpi(lineDesc.vrStart); 

                        // Return only TopLeft and Height.
                        // Adjust rect.Left by taking into account flow direction of the
                        // content and orientation of input position. 
                        if (ThisFlowDirection != flowDirection)
                        { 
                            if (orientation == LogicalDirection.Forward) 
                            {
                                rect.X = rect.Right; 
                            }
                        }
                        else
                        { 
                            // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning
                            //       content with Backward orientation. This should not be a valid position. 
                            //       Remove it later 
                            if (orientation == LogicalDirection.Backward && originalDcp > 0 && (context == TextPointerContext.Text || context == TextPointerContext.EmbeddedElement))
                            { 
                                rect.X = rect.Right;
                            }
                        }
                        rect.Width = 0; 

                        vrBaseline = line.Baseline + lineDesc.vrStart; 
 
                        // Dispose the line
                        line.Dispose(); 
                        break;
                    }
                }
            } 
        }
 
        // ----------------------------------------------------------------- 
        // Returns tight bounding path geometry for simple lines
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical] 
        private Geometry PathGeometryFromDcpRangeSimpleLines(
            int dcpStart, 
            int dcpEnd, 
            double paragraphTopSpace,
            bool handleEndOfPara, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            Rect visibleRect)
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            Geometry geometry = null; 
 
            //  get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            int lineStart = 0;
            int lineCount = arrayLineDesc.Length; 

            if(_lineIndexFirstVisual != -1) 
            { 
                lineStart = _lineIndexFirstVisual;
                lineCount = _visual.Children.Count; 
            }

            for (int lineIndex = lineStart; lineIndex < (lineStart + lineCount); ++lineIndex)
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex];
 
                if (handleEndOfPara) 
                {
                    //  Note (end-of-para workaround): '<' gives the chance 
                    //  for the EOP handling code below to emulate EOP glyph
                    if (dcpEnd < lineDesc.dcpFirst)
                    {
                        //  this line starts after the range's end. 
                        //  safe to break from the loop.
                        break; 
                    } 
                }
                else 
                {
                    if (dcpEnd <= lineDesc.dcpFirst)
                    {
                        //  this line starts after the range's end. 
                        //  safe to break from the loop.
                        break; 
                    } 
                }
 
                //  'dcp' needs to be within line range. If position points to dcpLim,
                //  it means that the next line starts from such position, hence go to the next line.
                //  But if this is the last line (EOP character), get geometry form the last
                //  character of the line. 
                if (    lineDesc.dcpLim > dcpStart
                    ||  (   (lineIndex == arrayLineDesc.Length - 1) 
                        &&  (lineDesc.dcpLim == dcpStart)   ) 
                   )
                { 
                    int dcpRangeStartForThisLine = Math.Max(lineDesc.dcpFirst, dcpStart);
                    //  Note (end-of-para workaround): dcp can be '0' due to end-of-para
                    //  not included into cp count - but it is there!!!
                    int cchRangeForThisLine = Math.Max(Math.Min(lineDesc.dcpLim, dcpEnd) - dcpRangeStartForThisLine, 1); 
                    double lineTopSpace = (lineIndex == 0) ? paragraphTopSpace : 0.0;
                    double endOfParaGlyphWidth; 
 
                    if (    (handleEndOfPara && lineIndex == (arrayLineDesc.Length - 1))
                        ||  (dcpEnd >= lineDesc.dcpLim && HasAnyLineBreakAtCp(lineDesc.dcpLim)) ) 
                    {
                        endOfParaGlyphWidth = ((double)TextParagraph.Element.GetValue(TextElement.FontSizeProperty) * CaretElement.c_endOfParaMagicMultiplier);
                    }
                    else 
                    {
                        endOfParaGlyphWidth = 0; 
                    } 

                    //  get rectangles for this line. 
                    IList rectangles = RectanglesFromDcpRangeOfSimpleLine(
                            dcpRangeStartForThisLine,
                            cchRangeForThisLine,
                            lineTopSpace, 
                            endOfParaGlyphWidth,
                            ref lineDesc, 
                            lineIndex, 
                            visibleRect
                            ); 

                    if (rectangles != null)
                    {
                        for (int i = 0, count = rectangles.Count; i < count; ++i) 
                        {
                            RectangleGeometry rectGeometry = new RectangleGeometry(rectangles[i]); 
                            CaretElement.AddGeometry(ref geometry, rectGeometry); 
                        }
                    } 
                }
            }

            return geometry; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns tight bounding path geometry for composite lines
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine.
        ///  
        [SecurityCritical]
        private Geometry PathGeometryFromDcpRangeCompositeLines( 
            int dcpStart, 
            int dcpEnd,
            double paragraphTopSpace, 
            bool handleEndOfPara,
            ref PTS.FSTEXTDETAILSFULL textDetails,
            Rect visibleRect)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            Geometry geometry = null; 

            //  get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            for (int lineIndex = 0; lineIndex < arrayLineDesc.Length; ++lineIndex) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[lineIndex]; 
                if (lineDesc.cElements == 0) 
                {
                    continue; 
                }

                //  get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elemIndex = 0; elemIndex < arrayLineElement.Length; ++elemIndex) 
                {
                    PTS.FSLINEELEMENT elemDesc = arrayLineElement[elemIndex]; 

                    if (handleEndOfPara)
                    {
                        //  Note (end-of-para workaround): '<' gives the chance 
                        //  for the EOP handling code below to emulate EOP glyph
                        if (dcpEnd < elemDesc.dcpFirst) 
                        { 
                            //  this line starts after the range's end.
                            //  safe to break from the loop. 
                            break;
                        }
                    }
                    else 
                    {
                        if (dcpEnd <= elemDesc.dcpFirst) 
                        { 
                            //  this line starts after the range's end.
                            //  safe to break from the loop. 
                            break;
                        }
                    }
 
                    //  'dcp' needs to be within line range. If position points to dcpLim,
                    //  it means that the next line starts from such position, hence go to the next line. 
                    //  But if this is the last line (EOP character), get geometry form the last 
                    //  character of the line.
                    if (    elemDesc.dcpLim > dcpStart 
                        ||  (   (elemDesc.dcpLim == dcpStart)
                            &&  (elemIndex == arrayLineElement.Length - 1)
                            &&  (lineIndex == arrayLineDesc.Length - 1) )   )
                    { 
                        int dcpRangeStartForThisElem = Math.Max(elemDesc.dcpFirst, dcpStart);
                        //  Note (end-of-para workaround): dcp can be '0' due to end-of-para 
                        //  not included into cp count - but it is there!!! 
                        int cchRangeForThisElem = Math.Max(Math.Min(elemDesc.dcpLim, dcpEnd) - dcpRangeStartForThisElem, 1);
                        double lineTopSpace = (lineIndex == 0) ? paragraphTopSpace : 0.0; 
                        double endOfParaGlyphWidth;

                        if (    (handleEndOfPara && lineIndex == (arrayLineDesc.Length - 1))
                            ||  (dcpEnd >= elemDesc.dcpLim && HasAnyLineBreakAtCp(elemDesc.dcpLim)  )   ) 
                        {
                            endOfParaGlyphWidth = ((double)TextParagraph.Element.GetValue(TextElement.FontSizeProperty) * CaretElement.c_endOfParaMagicMultiplier); 
                        } 
                        else
                        { 
                            endOfParaGlyphWidth = 0;
                        }

                        //  get rectangles for this element. 
                        IList rectangles = RectanglesFromDcpRangeOfCompositeLineElement(
                                dcpRangeStartForThisElem, 
                                cchRangeForThisElem, 
                                lineTopSpace,
                                endOfParaGlyphWidth, 
                                ref lineDesc,
                                lineIndex,
                                ref elemDesc,
                                elemIndex, 
                                visibleRect
                                ); 
 
                        if (rectangles != null)
                        { 
                            for (int i = 0, count = rectangles.Count; i < count; ++i)
                            {
                                RectangleGeometry rectGeometry = new RectangleGeometry(rectangles[i]);
                                CaretElement.AddGeometry(ref geometry, rectGeometry); 
                            }
                        } 
                    } 
                }
            } 

            return geometry;
        }
 
        // -----------------------------------------------------------------
        //  Helper to check if there is any line break at the given dcp. 
        //  Dcp adjusted to this paragraph. 
        // ------------------------------------------------------------------
        private bool HasAnyLineBreakAtCp(int dcp) 
        {
            ITextPointer position = Paragraph.StructuralCache.TextContainer.CreatePointerAtOffset(Paragraph.ParagraphStartCharacterPosition + dcp, LogicalDirection.Forward);
            //
            return (TextPointerBase.IsNextToAnyBreak(position, LogicalDirection.Backward)); 
        }
 
        // ----------------------------------------------------------------- 
        //  Returns rectangles for a single simple line correcsponding to the
        //  given dcp range. Includes trailing whitespaces. 
        //  Params:
        //      dcpRangeStart     - range's cp start position. Adjusted for
        //                          line's cp range.
        //      cchRange          - nuber of cps in the range. 
        //      lineTopSpace      - the value that line's height should
        //                          be extended to at the top. 
        //      lineRightSpace    - the value that line's width should 
        //                          be extended to at the right.
        //      lineDesc          - line description. 
        //      lineIndex         - line index.
        //      visibleRect       - visibility rectangle. It is Ok to return
        //                          null if the line is not visible.
        //      hasAttachedObjects- attached objects are present. 
        //  Returns:
        //      null              - if line is not visible 
        //      rectangles        - otherwise. 
        // ------------------------------------------------------------------
        private List RectanglesFromDcpRangeOfSimpleLine( 
            int dcpRangeStart,
            int cchRange,
            double lineTopSpace,
            double lineRightSpace, 
            ref PTS.FSLINEDESCRIPTIONSINGLE lineDesc,
            int lineIndex, 
            Rect visibleRect) 
        {
            List rectangles = null; 

            Invariant.Assert(lineDesc.dcpFirst <= dcpRangeStart && dcpRangeStart <= lineDesc.dcpLim && cchRange > 0);

            Rect lineRect = new PTS.FSRECT(lineDesc.urBBox, lineDesc.vrStart, lineDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); 

            //  width has to be adjusted to include trailing whitespaces... 
            LineVisual lineVisual = FetchLineVisual(lineIndex); 
            if (lineVisual != null)
            { 
                lineRect.Width = lineVisual.WidthIncludingTrailingWhitespace;
            }

            lineRect.Y = lineRect.Y - lineTopSpace; 
            lineRect.Height = lineRect.Height + lineTopSpace;
            lineRect.Width = lineRect.Width + lineRightSpace; 
 
            // Ignore horizontal offset because TextBox page width != extent width.
            // It's ok to include content that doesn't strictly intersect -- this 
            // is a perf optimization and the edge cases won't significantly hurt us.
            Rect testRect = lineRect;
            testRect.X = visibleRect.X;
 
            if (testRect.IntersectsWith(visibleRect))
            { 
                // Check whether the line is fully selected - we don't need to reformat it in this case 
                if (dcpRangeStart == lineDesc.dcpFirst && lineDesc.dcpLim <= (dcpRangeStart + cchRange))
                { 
                    rectangles = new List(1);
                    rectangles.Add(lineRect);
                }
                else 
                {
                    //  create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    if (IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 
 
                    double duOffset = TextDpi.FromTextDpi(lineDesc.urStart);
                    double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart); 

                    rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset);

                    if (!DoubleUtil.IsZero(lineTopSpace)) 
                    {
                        for (int i = 0, count = rectangles.Count; i < count; ++i) 
                        { 
                            Rect r = rectangles[i];
                            r.Y = r.Y - lineTopSpace; 
                            r.Height = r.Height + lineTopSpace;
                            rectangles[i] = r;
                        }
                    } 

                    if (!DoubleUtil.IsZero(lineRightSpace)) 
                    { 
                        // add the rect representing end-of-line / end-of-para
                        rectangles.Add( 
                            new Rect(
                                duOffset + TextDpi.FromTextDpi(line.Start + line.Width),
                                dvOffset - lineTopSpace,
                                lineRightSpace, 
                                TextDpi.FromTextDpi(line.Height) + lineTopSpace
                                ) 
                            ); 
                    }
 
                    //  dispose of the line
                    line.Dispose();
                }
            } 

            return (rectangles); 
        } 

        // ------------------------------------------------------------------ 
        //  Returns rectangles for a single composite line correcsponding to the
        //  given dcp range. Includes trailing whitespaces.
        //  Params:
        //      dcpRangeStart     - range's cp start position. Adjusted for 
        //                          line's cp range.
        //      cchRange          - nuber of cps in the range. 
        //      lineTopSpace      - the value that line's height should 
        //                          be extended to at the top.
        //      lineRightSpace    - the value that line's width should 
        //                          be extended to at the right.
        //      lineDesc          - line description.
        //      lineIndex         - line index.
        //      elemDesc          - element description. 
        //      visibleRect       - visibility rectangle. It is Ok to return
        //                          null if the line is not visible. 
        //      hasAttachedObjects- Attached objects are present 
        //  Returns:
        //      null              - if line is not visible 
        //      rectangles        - otherwise.
        // -----------------------------------------------------------------
        private List RectanglesFromDcpRangeOfCompositeLineElement(
            int dcpRangeStart, 
            int cchRange,
            double lineTopSpace, 
            double lineRightSpace, 
            ref PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc,
            int lineIndex, 
            ref PTS.FSLINEELEMENT elemDesc,
            int elemIndex,
            Rect visibleRect)
        { 
            List rectangles = null;
 
            Rect elementRect = new PTS.FSRECT(elemDesc.urBBox, lineDesc.vrStart, elemDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); 

            //  width has to be adjusted to include trailing whitespaces... 
            LineVisual lineVisual = FetchLineVisualComposite(lineIndex, elemIndex);
            if (lineVisual != null)
            {
                elementRect.Width = lineVisual.WidthIncludingTrailingWhitespace; 
            }
 
            elementRect.Y = elementRect.Y - lineTopSpace; 
            elementRect.Height = elementRect.Height + lineTopSpace;
            elementRect.Width = elementRect.Width + lineRightSpace; 

            // Ignore horizontal offset because TextBox page width != extent width.
            // It's ok to include content that doesn't strictly intersect -- this
            // is a perf optimization and the edge cases won't significantly hurt us. 
            Rect testRect = elementRect;
            testRect.X = visibleRect.X; 
 
            if (testRect.IntersectsWith(visibleRect))
            { 
                // Check whether the line is fully selected - we don't need to reformat it in this case
                if (dcpRangeStart == elemDesc.dcpFirst && elemDesc.dcpLim <= (dcpRangeStart + cchRange))
                {
                    rectangles = new List(1); 
                    rectangles.Add(elementRect);
                } 
                else 
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(elemDesc.fClearOnLeft), PTS.ToBoolean(elemDesc.fClearOnRight), TextParagraph.TextRunCache);
                    if (IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = elemDesc.dcpLim - elemDesc.dcpFirst;
                    } 
                    TextParagraph.FormatLineCore(line, elemDesc.pfsbreakreclineclient, ctx, elemDesc.dcpFirst, elemDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), elemDesc.dcpFirst); 
                    Invariant.Assert(line.SafeLength == elemDesc.dcpLim - elemDesc.dcpFirst, "Line length is out of [....]");
 
                    double duOffset = TextDpi.FromTextDpi(elemDesc.urStart);
                    double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart);

                    rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset); 

                    if (!DoubleUtil.IsZero(lineTopSpace)) 
                    { 
                        for (int i = 0, count = rectangles.Count; i < count; ++i)
                        { 
                            Rect r = rectangles[i];
                            r.Y = r.Y - lineTopSpace;
                            r.Height = r.Height + lineTopSpace;
                            rectangles[i] = r; 
                        }
                    } 
 
                    if (!DoubleUtil.IsZero(lineRightSpace))
                    { 
                        // add the rect representing end-of-line / end-of-para
                        rectangles.Add(
                            new Rect(
                                duOffset + TextDpi.FromTextDpi(line.Start + line.Width), 
                                dvOffset - lineTopSpace,
                                lineRightSpace, 
                                TextDpi.FromTextDpi(line.Height) + lineTopSpace 
                                )
                            ); 
                    }

                    //  dispose of the line
                    line.Dispose(); 
                }
            } 
 
            return (rectangles);
        } 

        // ------------------------------------------------------------------
        //  Helper to return visual corresponding to a line in
        //  FSLINEDESCRIPTIONSINGLE array. 
        // -----------------------------------------------------------------
        private LineVisual FetchLineVisual(int index) 
        { 
            LineVisual visual = null;
 
            int count = VisualTreeHelper.GetChildrenCount(Visual);

            if (count != 0)
            { 
                int visualIndex = index;
 
                if (_lineIndexFirstVisual != -1) 
                {
                    visualIndex -= _lineIndexFirstVisual; 
                }

                if (0 <= visualIndex && visualIndex < count)
                { 
                    visual = VisualTreeHelper.GetChild(Visual, visualIndex) as LineVisual;
                    //  verify that our assumptions about visual structure is correct... 
                    Invariant.Assert(visual != null || VisualTreeHelper.GetChild(Visual, visualIndex) == null); 
                }
            } 

            return (visual);
        }
 
        // -----------------------------------------------------------------
        //  Helper to return visual corresponding to a line in 
        //  FSLINEDESCRIPTIONSINGLE array. 
        // -----------------------------------------------------------------
        private LineVisual FetchLineVisualComposite(int lineIndex, int elemIndex) 
        {
            LineVisual visual = null;
            Visual temp = Visual;
            int count = VisualTreeHelper.GetChildrenCount(Visual); 

            if (count != 0) 
            { 
                int visualIndex = lineIndex;
 
                if(VisualTreeHelper.GetChild(Visual, visualIndex) is ParagraphElementVisual)
                {
                    temp = Visual.InternalGetVisualChild(lineIndex);
                    visualIndex = elemIndex; 
                }
 
                visual = VisualTreeHelper.GetChild(temp, visualIndex) as LineVisual; 
                //  verify that our assumptions about visual structure is correct...
                Invariant.Assert(visual != null || VisualTreeHelper.GetChild(temp, visualIndex) == null); 
            }

            return (visual);
        } 

        // ------------------------------------------------------------------ 
        // Returns tight bounding path geometry for 
        // floaters and figures objects
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.AttachedObjectListFromParagraph
        /// 
        [SecurityCritical] 
        private Geometry PathGeometryFromDcpRangeFloatersAndFigures(
            int dcpStart, 
            int dcpEnd, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            Geometry geometry = null;

            if (textDetails.cAttachedObjects > 0)
            { 
                //  get list of attached objects
                PTS.FSATTACHEDOBJECTDESCRIPTION[] arrayAttachedObjectDesc; 
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.cAttachedObjects, out arrayAttachedObjectDesc); 

                for (int index = 0; index < arrayAttachedObjectDesc.Length; ++index) 
                {
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];

                    BaseParaClient objectParaClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(objectParaClient);
 
                    BaseParagraph objectPara = objectParaClient.Paragraph; 

                    if (dcpEnd <= objectPara.ParagraphStartCharacterPosition) 
                    {
                        break;
                    }
 
                    if (objectPara.ParagraphEndCharacterPosition > dcpStart)
                    { 
                        Rect objectRect = objectParaClient.Rect.FromTextDpi(); 
                        RectangleGeometry objectGeometry = new RectangleGeometry(objectRect);
                        CaretElement.AddGeometry(ref geometry, objectGeometry); 
                    }
                }
            }
 
            return (geometry);
        } 
 
        // ------------------------------------------------------------------
        // Return true if caret is at unit boundary 
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara.
        ///  
        [SecurityCritical]
        private bool IsAtCaretUnitBoundaryFromDcpSimpleLines( 
            int dcp, 
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            bool isAtCaretUnitBoundary = false;
 
            // First iterate through lines
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                // 'dcp' needs to be within line range. If position points to dcpLim, 
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                {
                    CharacterHit charHit = new CharacterHit();
                    if (dcp >= lineDesc.dcpLim - 1 && index == arrayLineDesc.Length - 1)
                    { 
                        // Special case: last line has additional character to mark the end of paragraph.
                        // We should not try and check for next source character index 
                        // But just return true in this case 
                        return true;
                    } 

                    if (position.LogicalDirection == LogicalDirection.Backward)
                    {
                        if (lineDesc.dcpFirst == dcp) 
                        {
                            if (index == 0) 
                            { 
                                // First position of first line does not have a trailing edge. Return false.
                                return false; 
                            }
                            else
                            {
                                // Get the trailing edge of the last character on the previous line, at dcp - 1 
                                index--;
                                lineDesc = arrayLineDesc[index]; 
                                Invariant.Assert(dcp > 0); 
                                charHit = new CharacterHit(dcp - 1, 1);
                            } 
                        }
                        else
                        {
                            // Get CharacterHit at trailing edge of previous position 
                            Invariant.Assert(dcp > 0);
                            charHit = new CharacterHit(dcp - 1, 1); 
                        } 
                    }
                    else if (position.LogicalDirection == LogicalDirection.Forward) 
                    {
                        // Get character hit at leading edge
                        charHit = new CharacterHit(dcp, 0);
                    } 

                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
 
                    if(IsOptimalParagraph)
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 
 
                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 
                    isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit);

                    // Dispose the line
                    line.Dispose(); 
                    break;
                } 
            } 
            return isAtCaretUnitBoundary;
        } 

        // -----------------------------------------------------------------
        // Return true if caret is at unit boundary
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        ///  
        [SecurityCritical]
        private bool IsAtCaretUnitBoundaryFromDcpCompositeLines( 
            int dcp,
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            bool isAtCaretUnitBoundary = false;

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0)
                { 
                    continue;
                }

                // Get list of line elements. 
                PTS.FSLINEELEMENT[] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                { 
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line. 
                    if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp))
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    { 
                        CharacterHit charHit = new CharacterHit();
                        if (dcp >= element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) 
                        {
                            // Special case: at the end of the last line there is a special character that
                            // does not belong to the line.  Return true for this case
                            return true; 
                        }
 
                        if (position.LogicalDirection == LogicalDirection.Backward) 
                        {
                            // Beginning of element. 
                            if (dcp == element.dcpFirst)
                            {
                                if (elIndex > 0)
                                { 
                                    // Beginning of element, but not of line. Create char hit at last dcp of previous element, trailing edge.
                                    --elIndex; 
                                    element = arrayLineElement[elIndex]; 
                                    charHit = new CharacterHit(dcp - 1, 1);
                                } 
                                else
                                {
                                    // Beginning of line
                                    if (index == 0) 
                                    {
                                        // Backward context at start position of first line is not considered a unit boundary 
                                        return false; 
                                    }
                                    else 
                                    {

                                        // Go to previous line
                                        --index; 
                                        lineDesc = arrayLineDesc[index];
                                        if (lineDesc.cElements == 0) 
                                        { 
                                            return false;
                                        } 
                                        else
                                        {
                                            // Get list of line elements.
                                            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
                                            element = arrayLineElement[arrayLineElement.Length - 1];
                                            charHit = new CharacterHit(dcp - 1, 1); 
                                        } 
                                    }
                                } 
                            }
                            else
                            {
                                // Get trailing edge of previous dcp 
                                Invariant.Assert(dcp > 0);
                                charHit = new CharacterHit(dcp - 1, 1); 
                            } 
                        }
                        else if (position.LogicalDirection == LogicalDirection.Forward) 
                        {
                            // Create character hit at leading edge
                            charHit = new CharacterHit(dcp, 0);
                        } 

                        // Create and format line 
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
 
                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 
 
                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 
                        isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit);

                        // Dispose the line
                        line.Dispose(); 
                        return isAtCaretUnitBoundary;
                    } 
                } 
            }
            return isAtCaretUnitBoundary; 
        }

        // -----------------------------------------------------------------
        // Get Next caret unit position 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer NextCaretUnitPositionFromDcpSimpleLines(
            int dcp,
            ITextPointer position,
            LogicalDirection direction, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare next position and set it to initial position 
            ITextPointer nextCaretPosition = position;
 
            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) 
                { 
                    if (dcp == lineDesc.dcpFirst && direction == LogicalDirection.Backward)
                    { 
                        // Go to previous line
                        if (index == 0)
                        {
                            return position; 
                        }
                        else 
                        { 
                            // Update dcp, lineDesc
                            Debug.Assert(index > 0); 
                            --index;
                            lineDesc = arrayLineDesc[index];
                        }
                    } 
                    else if (dcp >= lineDesc.dcpLim - 1 && direction == LogicalDirection.Forward)
                    { 
                        // If we are at the last line there will be a fake marker for this, so we return 
                        if (index == arrayLineDesc.Length - 1)
                        { 
                            return position;
                        }
                    }
 
                    // Create and format line
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

                    if(IsOptimalParagraph) 
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);
 
                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Create CharacterHit
                    CharacterHit charHit = new CharacterHit(dcp, 0);

                    // Get previous caret position from the line 
                    // Determine logical direction for next caret index and create TextPointer from it
                    CharacterHit nextCharacterHit; 
                    if (direction == LogicalDirection.Forward) 
                    {
                        nextCharacterHit = line.GetNextCaretCharacterHit(charHit); 
                    }
                    else
                    {
                        nextCharacterHit = line.GetPreviousCaretCharacterHit(charHit); 
                    }
 
                    LogicalDirection logicalDirection; 
                    if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == lineDesc.dcpLim) && direction == LogicalDirection.Forward)
                    { 
                        // Going forward brought us to the end of a line, context must be forward for next line
                        if (index == arrayLineDesc.Length - 1)
                        {
                            // last line so context must stay backward 
                            logicalDirection = LogicalDirection.Backward;
                        } 
                        else 
                        {
                            logicalDirection = LogicalDirection.Forward; 
                        }
                    }
                    else if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == lineDesc.dcpFirst) && direction == LogicalDirection.Backward)
                    { 
                        // Going forward brought us to the start of a line, context must be backward for previous line
                        if (index == 0) 
                        { 
                            // First line, so we will stay forward
                            logicalDirection = LogicalDirection.Forward; 
                        }
                        else
                        {
                            logicalDirection = LogicalDirection.Backward; 
                        }
                    } 
                    else 
                    {
                        logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; 
                    }
                    nextCaretPosition = GetTextPosition(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection);

                    // Dispose the line 
                    line.Dispose();
                    break; 
                } 
            }
            return nextCaretPosition; 
        }

        // -----------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer NextCaretUnitPositionFromDcpCompositeLines(
            int dcp,
            ITextPointer position,
            LogicalDirection direction, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines 
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare next position and set it to initial position 
            ITextPointer nextCaretPosition = position;
 
            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements. 
                PTS.FSLINEELEMENT [] arrayLineElement;
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                { 
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line. 
                    if (   ((element.dcpFirst <= dcp) && (element.dcpLim > dcp))
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) 
                    { 
                        if (dcp == element.dcpFirst && direction == LogicalDirection.Backward)
                        { 
                            // Beginning of element.
                            if (dcp == 0)
                            {
                                // Assert that this is first elment on first line 
                                Debug.Assert(index == 0);
                                Debug.Assert(elIndex == 0); 
                                return position; 
                            }
                            else 
                            {
                                if (elIndex > 0)
                                {
                                    // Beginning of element, but not of line 
                                    --elIndex;
                                    element = arrayLineElement[elIndex]; 
                                } 
                                else
                                { 
                                    // There must be at least one line above this
                                    Debug.Assert(index > 0);

                                    // Go to previous line 
                                    --index;
                                    lineDesc = arrayLineDesc[index]; 
                                    if (lineDesc.cElements == 0) 
                                    {
                                        // Stay in same position 
                                        return position;
                                    }
                                    else
                                    { 

                                        // Get list of line elements. 
                                        PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 
                                        element = arrayLineElement[arrayLineElement.Length - 1];
                                    } 
                                }
                            }
                        }
                        else if (dcp >= element.dcpLim - 1 && direction == LogicalDirection.Forward) 
                        {
                            if (dcp == element.dcpLim) 
                            { 
                                // End of paragraph
                                Debug.Assert(elIndex == arrayLineElement.Length - 1); 
                                Debug.Assert(index == arrayLineDesc.Length - 1);
                                return position;
                            }
                            else if (dcp == element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) 
                            {
                                // Special end character does not belong to line 
                                return position; 
                            }
                        } 

                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                        if(IsOptimalParagraph) 
                        { 
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        } 

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Create CharacterHit from dcp, and get next 
                        CharacterHit charHit = new CharacterHit(dcp, 0);
                        CharacterHit nextCharacterHit; 
                        if (direction == LogicalDirection.Forward)
                        {
                            nextCharacterHit = line.GetNextCaretCharacterHit(charHit);
                        } 
                        else
                        { 
                            nextCharacterHit = line.GetPreviousCaretCharacterHit(charHit); 
                        }
 
                        LogicalDirection logicalDirection;
                        if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == element.dcpLim) && direction == LogicalDirection.Forward)
                        {
                            // Going forward brought us to the end of a line, context must be forward for next line 
                            if (index == arrayLineDesc.Length - 1)
                            { 
                                // last line so context must stay backward 
                                logicalDirection = LogicalDirection.Backward;
                            } 
                            else
                            {
                                // It is a new element, on the same line or a new one. Either way it;s forward context
                                logicalDirection = LogicalDirection.Forward; 
                            }
                        } 
                        else if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == element.dcpFirst) && direction == LogicalDirection.Backward) 
                        {
                            // Going forward brought us to the start of a line, context must be backward for previous line 
                            if (index == 0)
                            {
                                // First line, so we will stay forward
                                logicalDirection = LogicalDirection.Forward; 
                            }
                            else 
                            { 
                                // Either the previous element or last element on previous line, context is backward
                                logicalDirection = LogicalDirection.Backward; 
                            }
                        }
                        else
                        { 
                            logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
                        } 
                        nextCaretPosition = GetTextPosition(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection); 

                        // Dispose the line 
                        line.Dispose();
                        return nextCaretPosition;
                    }
                } 
            }
            return nextCaretPosition; 
        } 

        // ----------------------------------------------------------------- 
        // Get Backspace caret unit position
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls the Critical function PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer BackspaceCaretUnitPositionFromDcpSimpleLines( 
            int dcp,
            ITextPointer position, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // Declare backspace position and set it to initial position 
            ITextPointer backspaceCaretPosition = position;

            // First iterate through lines
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
 
                // 'dcp' needs to be within line range. If position points to dcpLim,
                // it means that the next line starts from such position, hence go to the next line. 
                if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp))
                    || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1)))
                {
                    if (dcp == lineDesc.dcpFirst) 
                    {
                        // Go to previous line 
                        if (index == 0) 
                        {
                            return position; 
                        }
                        else
                        {
                            // Update dcp, lineDesc 
                            Debug.Assert(index > 0);
                            --index; 
                            lineDesc = arrayLineDesc[index]; 
                        }
                    } 

                    // Create and format line
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

                    if(IsOptimalParagraph) 
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                    // Create CharacterHit and get backspace index from line API 
                    CharacterHit textSourceCharacterIndex = new CharacterHit(dcp, 0);
                    CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex); 
                    LogicalDirection logicalDirection;
                    if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == lineDesc.dcpFirst)
                    {
                        // Going forward brought us to the start of a line, context must be backward for previous line 
                        if (index == 0)
                        { 
                            // First line, so we will stay forward 
                            logicalDirection = LogicalDirection.Forward;
                        } 
                        else
                        {
                            logicalDirection = LogicalDirection.Backward;
                        } 
                    }
                    else 
                    { 
                        logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
                    } 
                    backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection);

                    // Dispose the line
                    line.Dispose(); 
                    break;
                } 
            } 
            Debug.Assert(backspaceCaretPosition != null);
            return backspaceCaretPosition; 
        }

        // ------------------------------------------------------------------
        // Retrieve bounds of an object/character at specified text position. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical function PtsHelper.LineListCompositeFromTextPara. 
        /// 
        [SecurityCritical] 
        private ITextPointer BackspaceCaretUnitPositionFromDcpCompositeLines(
            int dcp,
            ITextPointer position,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

            // Declare backspace position and set it to initial position
            ITextPointer backspaceCaretPosition = position; 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // 'dcp' needs to be within line range. If position points to dcpLim,
                    // it means that the next line starts from such position, hence go to the next line.
                    if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) 
                        || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1)))
                    { 
                        if (dcp == element.dcpFirst) 
                        {
                            // Beginning of element. 
                            if (dcp == 0)
                            {
                                // Assert that this is first elment on first line
                                Debug.Assert(index == 0); 
                                Debug.Assert(elIndex == 0);
                                return position; 
                            } 
                            else
                            { 
                                if (elIndex > 0)
                                {
                                    // Beginning of element, but not of line
                                    --elIndex; 
                                    element = arrayLineElement[elIndex];
                                } 
                                else 
                                {
                                    // There must be at least one line above this 
                                    Debug.Assert(index > 0);

                                    // Go to previous line
                                    --index; 
                                    lineDesc = arrayLineDesc[index];
                                    if (lineDesc.cElements == 0) 
                                    { 
                                        // Stay in same position
                                        return position; 
                                    }
                                    else
                                    {
                                        // Get list of line elements. 
                                        PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
                                        element = arrayLineElement[arrayLineElement.Length - 1]; 
                                    } 
                                }
                            } 
                        }

                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
 
                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);
 
                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 
 
                        // Create CharacterHit from dcp, and get backspace
                        CharacterHit charHit = new CharacterHit(dcp, 0); 
                        CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(charHit);
                        LogicalDirection logicalDirection;
                        if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == element.dcpFirst)
                        { 
                            // Going forward brought us to the start of a line, context must be backward for previous line
                            if (index == 0) 
                            { 
                                // First line, so we will stay forward
                                logicalDirection = LogicalDirection.Forward; 
                            }
                            else
                            {
                                logicalDirection = LogicalDirection.Backward; 
                            }
                        } 
                        else 
                        {
                            logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; 
                        }
                        backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection);

                        // Dispose the line 
                        line.Dispose();
                        return backspaceCaretPosition; 
                    } 
                }
            } 
            return backspaceCaretPosition;
        }

        // ------------------------------------------------------------------ 
        // Retrieves collection of GlyphRuns from a range of text.
        // ----------------------------------------------------------------- 
        ///  
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        ///  
        [SecurityCritical]
        private void GetGlyphRunsFromSimpleLines(
            List glyphRuns,
            int dcpStart, 
            int dcpEnd,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            // Get list of lines
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Iterate through all lines
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            { 
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                // Range (dcpStart...dcpEnd) needs to insersect with line's range.
                if (dcpStart < lineDesc.dcpLim && dcpEnd > lineDesc.dcpFirst)
                {
                    // Create and format line 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    }

                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                    // Assert that number of characters in Text line is the same as our expected length 
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Retrieve glyphs from this line 
                    line.GetGlyphRuns(glyphRuns, Math.Max(dcpStart, lineDesc.dcpFirst), Math.Min(dcpEnd, lineDesc.dcpLim));

                    // Dispose the line
                    line.Dispose(); 
                }
                // No need to continue, if dcpEnd has been reached. 
                if (dcpEnd < lineDesc.dcpLim) 
                    break;
            } 
        }

        // -----------------------------------------------------------------
        // Retrieves collection of GlyphRuns from a range of text. 
        // -----------------------------------------------------------------
        // ------------------------------------------------------------------ 
        ///  
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical]
        private void GetGlyphRunsFromCompositeLines(
            List glyphRuns, 
            int dcpStart,
            int dcpEnd, 
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            // Get list of lines
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // First iterate through lines 
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
                if (lineDesc.cElements == 0) { continue; }

                // Get list of line elements.
                PTS.FSLINEELEMENT[] arrayLineElement; 
                PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                {
                    PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                    // Range (dcpStart...dcpEnd) needs to insersect with line's range.
                    if (dcpStart < element.dcpLim && dcpEnd > element.dcpFirst)
                    { 
                        // Create and format line
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                        if(IsOptimalParagraph) 
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        }
 
                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);
 
                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                        // Retrieve glyphs from this line
                        line.GetGlyphRuns(glyphRuns, Math.Max(dcpStart, element.dcpFirst), Math.Min(dcpEnd, element.dcpLim));

                        // Dispose the line 
                        line.Dispose();
                    } 
                    // No need to continue, if dcpEnd has been reached. 
                    if (dcpEnd < element.dcpLim)
                        break; 
                }
            }
        }
 
        // -----------------------------------------------------------------
        // Render text paragraph content. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara 
        /// 
        [SecurityCritical]
        private void RenderSimpleLines(
            ContainerVisual visual, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            bool ignoreUpdateInfo) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; 

            VisualCollection visualChildren = visual.Children;

            // Get list of simple lines. 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            // Create lines and render them
            if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo) 
            {
                // There is no update information, hence need to recreate
                // visuals for all lines.
                visualChildren.Clear(); 

                for (int index = 0; index < arrayLineDesc.Length; index++) 
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                    // Create and format line
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Create and validate line's visual 
                    ContainerVisual lineVisual = line.CreateVisual(); 
                    visualChildren.Insert(index, lineVisual);
                    lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                    // Dispose the line
                    line.Dispose();
                } 
            }
            else 
            { 
                // Shift lines before change
                if(textDetails.dvrShiftBeforeChange != 0) 
                {
                    for(int index = 0; index < textDetails.cLinesBeforeChange; index++)
                    {
                        ContainerVisual lineVisual = (ContainerVisual) visualChildren[index]; 
                        Vector offset = lineVisual.Offset;
                        offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange); 
                        lineVisual.Offset = offset; 
                    }
                } 

                // Skip not changed lines
                // Remove changed lines.
                visualChildren.RemoveRange(textDetails.cLinesBeforeChange, textDetails.cLinesChanged - textDetails.dcLinesChanged); 
                // Add new lines
                for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++) 
                { 
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                    // Create and format line
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    { 
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; 
                    }
 
                    TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

                    // Assert that number of characters in Text line is the same as our expected length
                    Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

                    // Create and validate line's visual 
                    ContainerVisual lineVisual = line.CreateVisual(); 
                    visualChildren.Insert(index, lineVisual);
                    lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                    // Dispose the line
                    line.Dispose();
                } 
                // Shift remaining lines
                for (int index = textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index < arrayLineDesc.Length; index++) 
                { 
                    // Shift line's visual
                    ContainerVisual lineVisual = (ContainerVisual) visualChildren[index]; 
                    Vector offset = lineVisual.Offset;
                    offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange);
                    lineVisual.Offset = offset;
                } 
            }
        } 
 

        // ------------------------------------------------------------------ 
        // Determines whether this para intersects with the rect on v dimension
        // -----------------------------------------------------------------
        private bool IntersectsWithRectOnV(ref PTS.FSRECT rect)
        { 
            return ((_rect.v) <= (rect.v + rect.dv)) &&
                   ((_rect.v + _rect.dv) >= rect.v); 
        } 

        // ------------------------------------------------------------------ 
        // Determines whether this para is totally contained in the given rect on v dimension
        // -----------------------------------------------------------------
        private bool ContainedInRectOnV(ref PTS.FSRECT rect)
        { 
            return (rect.v <= (_rect.v)) &&
                   (rect.v + rect.dv >= (_rect.v + _rect.dv)); 
        } 

        // ----------------------------------------------------------------- 
        // From a line desc and the cp for the para start, formats a line and creates a visual
        // -----------------------------------------------------------------
        private ContainerVisual CreateLineVisual(ref PTS.FSLINEDESCRIPTIONSINGLE lineDesc, int cpTextParaStart)
        { 
            Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache);
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 
 
            if(IsOptimalParagraph)
            { 
                ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
            }

            TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

            // Assert that number of characters in Text line is the same as our expected length 
            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); 

            // Create and validate line's visual 
            ContainerVisual lineVisual = line.CreateVisual();

            // Dispose the line
            line.Dispose(); 

            return lineVisual; 
        } 

        // ------------------------------------------------------------------ 
        // Resyncs the committed visuals section with actual viewport visibility
        // -----------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListSimpleFromTextPara. 
        /// 
        [SecurityCritical] 
        private void UpdateViewportSimpleLines( 
            ContainerVisual visual,
            ref PTS.FSTEXTDETAILSFULL textDetails, 
            ref PTS.FSRECT viewport)
        {
            VisualCollection visualChildren = visual.Children;
 
            Debug.Assert(!PTS.ToBoolean(textDetails.fLinesComposite));
 
            try 
            {
                // Common case, invisible para - Clear our children, _lineIndexFirstVisual will be cleared later 
                if (!IntersectsWithRectOnV(ref viewport))
                {
                    visualChildren.Clear();
                } 
                else if (ContainedInRectOnV(ref viewport) && _lineIndexFirstVisual == 0 && visualChildren.Count == textDetails.cLines)
                { 
                    // Totally visible para 
                    // Nothing to do here, totally visible and lines are updated. Don't query line list
                } 
                else
                {
                    // Index of first visible line
                    int lineIndexFirstVisible = -1; 
                    // Index of first invisible line - MAY BE EQUAL TO COUNT.
                    int lineIndexFirstInvisible = -1; 
                    int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition; 

                    PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
                    PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);

                    // If this para is totally contained in the viewport, valid range is all lines.
                    if (ContainedInRectOnV(ref viewport)) 
                    {
                        lineIndexFirstVisible = 0; 
                        lineIndexFirstInvisible = textDetails.cLines; 
                    }
                    else 
                    {
                        // Subset is valid, walk the lines to determine the first (even partially) visible line
                        int lineIndex;
 
                        for (lineIndex = 0; lineIndex < arrayLineDesc.Length; lineIndex++)
                        { 
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex]; 

                            // Vrstart is top of line, not baseline. 
                            if ((lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent) > viewport.v)
                            {
                                break;
                            } 
                        }
 
                        // May be equal to count if no lines are visible 
                        lineIndexFirstVisible = lineIndex;
 
                        // Subset is valid, walk the lines to determine the first totally invisible line
                        for (lineIndex = lineIndexFirstVisible; lineIndex < arrayLineDesc.Length; lineIndex++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[lineIndex]; 

                            if ((lineDesc.vrStart) > (viewport.v + viewport.dv)) 
                            { 
                                break;
                            } 
                        }

                        // May be equal to count if the remainder of lines is visible.
                        lineIndexFirstInvisible = lineIndex; 
                    }
 
                    // If we have some committed range, but there's no overlap between the previously committed range and the new desired range, 
                    // Delete all existing lines.
                    if (_lineIndexFirstVisual != -1 && ((lineIndexFirstVisible > _lineIndexFirstVisual + visualChildren.Count) || 
                                                       (lineIndexFirstInvisible < _lineIndexFirstVisual)))
                    {
                        visualChildren.Clear();
                        _lineIndexFirstVisual = -1; 
                    }
 
                    // If no existing lines, interate over visible range and add appropriate lines. 
                    if (_lineIndexFirstVisual == -1)
                    { 
                        for (int index = lineIndexFirstVisible; index < lineIndexFirstInvisible; index++)
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                            ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart);
                            visualChildren.Add(lineVisual); 
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        }
                        _lineIndexFirstVisual = lineIndexFirstVisible; 
                    }
                    else if (lineIndexFirstVisible != _lineIndexFirstVisual || (lineIndexFirstInvisible - lineIndexFirstVisible) != visualChildren.Count)
                    {
                        // Need to resolve existing list with new list - [....] the beginning of the list 

 
                        //    |----------------| (Old committed range) 
                        // |-------|             (New committed range)
                        // Need to add visuals to the beginning to [....] the start position 
                        if (lineIndexFirstVisible < _lineIndexFirstVisual)
                        {
                            for (int index = lineIndexFirstVisible; index < _lineIndexFirstVisual; index++)
                            { 
                                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];
 
                                ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart); 

                                visualChildren.Insert(index - lineIndexFirstVisible, lineVisual); 
                                lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
                            }
                        }
                        else if (lineIndexFirstVisible != _lineIndexFirstVisual) 
                        {
                            //    |----------------| (Old committed range) 
                            //        |-------|             (New committed range) 
                            // Need to remove visuals from the beginning to [....] start positions.
 
                            visualChildren.RemoveRange(0, lineIndexFirstVisible - _lineIndexFirstVisual);
                        }
                        _lineIndexFirstVisual = lineIndexFirstVisible;
                    } 

                    Debug.Assert(_lineIndexFirstVisual == lineIndexFirstVisible); 
 

                    // Now [....] the end of the list, two cases.. 
                    // Fewer lines than existed before, remove lines from end
                    // |---------------|
                    // |----------|
                    if (lineIndexFirstInvisible - lineIndexFirstVisible < visualChildren.Count) 
                    {
                        int visualsToRemove = visualChildren.Count - (lineIndexFirstInvisible - lineIndexFirstVisible); 
 
                        visualChildren.RemoveRange(visualChildren.Count - visualsToRemove, visualsToRemove);
                    } 
                    else if ((lineIndexFirstInvisible - lineIndexFirstVisible) > visualChildren.Count)
                    {
                        // Or we need to add more lines to the end, format and insert those visuals
                        // |--------------| 
                        // |----------------------|
 
                        for (int index = _lineIndexFirstVisual + visualChildren.Count; index < lineIndexFirstInvisible; index++) 
                        {
                            PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 

                            ContainerVisual lineVisual = CreateLineVisual(ref lineDesc, cpTextParaStart);
                            visualChildren.Add(lineVisual);
                            lineVisual.Offset = new Vector(TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
                        }
                    } 
 
                    Debug.Assert(visualChildren.Count == (lineIndexFirstInvisible - lineIndexFirstVisible));
                } 
            }

            finally
            { 
                // Ensure the _lineIndexFirstVisual is syncced with visualChildren.Count
                if (visualChildren.Count == 0) 
                { 
                    _lineIndexFirstVisual = -1;
                } 
            }

#if VERIFY_VISUALS
            // Verify visuals against visuals list. 
            VerifyVisuals(ref textDetails);
#endif 
        } 

#if VERIFY_VISUALS 

        // Verifies all of our committed visuals are in the right order and agree with the locations of all of the full text details visuals
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListSimpleFromTextPara 
        /// Safe - The IntPtr parameters passed to PtsHelper.LineListSimpleFromTextPara are SecurityCriticalDataForSet
        ///        which ensures that partial trust code won't be able to set it to a random value. 
        ///        The textdetails coming as input parameter and is passed as is.  But this is still secure 
        ///        because of the way it's used inside PtsHelper.LineListSimpleFromTextPara. It's only
        //         used to create an array of size textDetails.cLines. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void VerifyVisuals(ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ContainerVisual lineContainerVisual = _visual;
 
            // Add visuals for floaters and figures. 
            if (textDetails.cFigures > 0 || textDetails.cFloaters > 0)
            { 
                lineContainerVisual = _visual.Children[0];
            }

            VisualCollection visualChildren = lineContainerVisual.Children; 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            if(_lineIndexFirstVisual == -1)
            { 
                Debug.Assert(visualChildren.Count == 0);
            }
            else
            { 
                Debug.Assert(visualChildren.Count > 0);
 
                for(int index = _lineIndexFirstVisual; index < _lineIndexFirstVisual + visualChildren.Count; index++) 
                {
                    PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                    Visual visualChild = visualChildren[index - _lineIndexFirstVisual];

                    Vector offset = visualChild.Offset;
                    int u = TextDpi.ToTextDpi(offset.X); 
                    int v = TextDpi.ToTextDpi(offset.Y);
 
                    Debug.Assert(u == (lineDesc.urStart) && v == (lineDesc.vrStart)); 
                }
            } 
        }

#endif
 
        // ------------------------------------------------------------------
        // Render composite lines. 
        // ------------------------------------------------------------------ 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara 
        ///            and PtsHelper.LineElementListFromCompositeLine.
        /// 
        [SecurityCritical]
        private void RenderCompositeLines( 
            ContainerVisual visual,
            ref PTS.FSTEXTDETAILSFULL textDetails, 
            bool ignoreUpdateInfo) 
        {
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); 

            VisualCollection visualChildren = visual.Children;
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;
 
            // Get list of composite lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 

            // Create lines and render them 
            if (!PTS.ToBoolean(textDetails.fUpdateInfoForLinesPresent) || ignoreUpdateInfo)
            {
                // There is no update information, hence need to recreate
                // visuals for all lines. 
                visualChildren.Clear();
 
                for (int index = 0; index < arrayLineDesc.Length; index++) 
                {
                    PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 

                    int visualIndex;
                    VisualCollection lineVisuals;
                    if (lineDesc.cElements == 1) 
                    {
                        visualIndex = index; 
                        lineVisuals = visualChildren; 
                    }
                    else 
                    {
                        visualIndex = 0;
                        ParagraphElementVisual lineVisual = new ParagraphElementVisual();
                        visualChildren.Add(lineVisual); 
                        lineVisuals = lineVisual.Children;
                    } 
 
                    // Get list of line elements
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);

                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                    { 
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];
 
                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 

                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }
 
                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                        // Create and validate line's visual
                        ContainerVisual lineVisual = line.CreateVisual(); 
                        lineVisuals.Insert(visualIndex + elIndex, lineVisual);
                        lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 
 
                        // Dispose the line
                        line.Dispose(); 
                    }
                }
            }
            else 
            {
                // Shift lines before change 
                if(textDetails.dvrShiftBeforeChange != 0) 
                {
                    for (int index = 0; index < textDetails.cLinesBeforeChange; index++) 
                    {
                        ContainerVisual lineVisual = (ContainerVisual) visualChildren[index];
                        Vector offset = lineVisual.Offset;
                        offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftBeforeChange); 
                        lineVisual.Offset = offset;
                    } 
                } 

                // Skip not changed lines 
                // Remove changed lines
                visualChildren.RemoveRange(textDetails.cLinesBeforeChange, textDetails.cLinesChanged - textDetails.dcLinesChanged);
                // Add new lines
                for (int index = textDetails.cLinesBeforeChange; index < textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index++) 
                {
                    PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; 
 
                    int visualIndex;
                    VisualCollection lineVisuals; 
                    if (lineDesc.cElements == 1)
                    {
                        visualIndex = index;
                        lineVisuals = visualChildren; 
                    }
                    else 
                    { 
                        visualIndex = 0;
                        ParagraphElementVisual lineVisual = new ParagraphElementVisual(); 
                        visualChildren.Add(lineVisual);
                        lineVisuals = lineVisual.Children;
                    }
 
                    // Get list of line elements.
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); 

                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) 
                    {
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];

                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 
 
                        if(IsOptimalParagraph)
                        { 
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst;
                        }

                        TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length 
                        Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 

                        // Create and validate line's visual 
                        ContainerVisual lineVisual = line.CreateVisual();
                        lineVisuals.Insert(visualIndex + elIndex, lineVisual);
                        lineVisual.Offset = new Vector(TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
 
                        // Dispose the line
                        line.Dispose(); 
                    } 
                }
                // Shift remaining lines 
                for (int index = textDetails.cLinesBeforeChange + textDetails.cLinesChanged; index < arrayLineDesc.Length; index++)
                {
                    ContainerVisual lineVisual = (ContainerVisual)visualChildren[index];
 
                    Vector offset = lineVisual.Offset;
                    offset.Y += TextDpi.FromTextDpi(textDetails.dvrShiftAfterChange); 
                    lineVisual.Offset = offset; 
                }
            } 
        }

        // -----------------------------------------------------------------
        // Render floaters and figures. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls some Critical PtsHelper functions. 
        /// Safe - as the IntPtr parameters passed in are Critical for set.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void ValidateVisualFloatersAndFigures(
            PTS.FSKUPDATE fskupdInherited,
            int cAttachedObjects) 
        {
            int index; 
            PTS.FSKUPDATE fskupd; 

            if (cAttachedObjects > 0) 
            {
                BaseParaClient paraClient;

                // Get list of floaters 
                PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc;
                PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, cAttachedObjects, 
                                                          out arrayAttachedObjectDesc); 

                // Render floaters. For each floater do following: 
                // (1) Retrieve ParaClient object
                // (2) Validate visual, if necessary
                for (index = 0; index < arrayAttachedObjectDesc.Length; index++)
                { 
                    PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index];
 
                    paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient);
 
                    fskupd = attachedObjectDesc.fsupdinf.fskupd;
                    if (fskupd == PTS.FSKUPDATE.fskupdInherited)
                    {
                        fskupd = fskupdInherited; 
                    }
 
                    if(fskupd != PTS.FSKUPDATE.fskupdNoChange) 
                    {
                        paraClient.ValidateVisual(fskupd); 
                    }
                }
            }
        } 

        // ----------------------------------------------------------------- 
        // Hittest simple lines. 
        // -----------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical function PtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical]
        private IInputElement InputHitTestSimpleLines( 
            PTS.FSPOINT pt,
            ref PTS.FSTEXTDETAILSFULL textDetails) 
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            IInputElement ie = null;
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;

            // Get list of complex lines. 
            PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc;
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); 
 
            // Find affected line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index];

                if (lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent > pt.v) 
                {
                    // Create and format line 
                    Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 
                    Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart);
 
                    if(IsOptimalParagraph)
                    {
                        ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
                    } 

 
                    using (line) 
                    {
                        TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); 

                        // Assert that number of characters in Text line is the same as our expected length
                        Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
                        if (lineDesc.urStart + line.CalculateUOffsetShift() <= pt.u && pt.u <= (lineDesc.urStart + line.CalculateUOffsetShift() + lineDesc.dur))
                        { 
                            int distance = pt.u - lineDesc.urStart; 

                            // Assert that number of characters in Text line is the same as our expected length 
                            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");

                            if ((line.Start <= distance) && (distance <= (line.Start + line.Width)))
                            { 
                                ie = line.InputHitTest(distance);
                            } 
                        } 
                    }
                    break; 
                }
            }
            return ie;
        } 

        // ----------------------------------------------------------------- 
        // We don't support deferred visuals in finite case, composite lines case, or figures/floaters/inline objects case 
        // ------------------------------------------------------------------
        private bool IsDeferredVisualCreationSupported(ref PTS.FSTEXTDETAILSFULL textDetails) 
        {
            if(!Paragraph.StructuralCache.IsDeferredVisualCreationSupported)
                return false;
 
            if(PTS.ToBoolean(textDetails.fLinesComposite))
                return false; 
 
            if(TextParagraph.HasFiguresFloatersOrInlineObjects())
                return false; 


            return true;
        } 

        // ----------------------------------------------------------------- 
        // GetRectangles in simple lines 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls Critical functionPtsHelper.LineListSimpleFromTextPara
        /// 
        [SecurityCritical]
        private List GetRectanglesInSimpleLines( 
            ContentElement e,
            int start, 
            int length, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            List rectangles = new List();
 
            // Calculate local element start by subtracting TextPara start relative to TextContainer
            // from element start 
            int localStart = start - Paragraph.ParagraphStartCharacterPosition; 

            if (localStart < 0) 
            {
                // May happen in case of figures, floaters
                return rectangles;
            } 

            // Get list of complex lines. 
            PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; 
            PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++)
            {
                PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; 
                List lineRectangles = GetRectanglesInSingleLine(lineDesc, e, localStart, length);
                Invariant.Assert(lineRectangles != null); 
                if (lineRectangles.Count != 0) 
                {
                    rectangles.AddRange(lineRectangles); 
                }
            }
            return rectangles;
        } 

        // ------------------------------------------------------------------ 
        // Returns ArrayList of rectangles (at msot one) for the given ContentElement 
        // e if it spans all or part of the specified line
        // ----------------------------------------------------------------- 
        private List GetRectanglesInSingleLine(
            PTS.FSLINEDESCRIPTIONSINGLE lineDesc,
            ContentElement e,
            int start, 
            int length)
        { 
            // Calculate end of element relative to TextParagraph by adding length to start position 
            int end = start + length;
            List rectangles = new List(); 

            // If the element does not lie in the line at all, return empty list
            if (start >= lineDesc.dcpLim)
            { 
                // Element starts after line ends
                return rectangles; 
            } 
            if (end <= lineDesc.dcpFirst)
            { 
                // Element ends before line starts
                return rectangles;
            }
 
            // Establish start and end points of element span within the line so that
            // we can get rectangle between them 
            int localStart = (start < lineDesc.dcpFirst) ? lineDesc.dcpFirst : start; 
            int localEnd = (end < lineDesc.dcpLim) ? end : lineDesc.dcpLim;
            Debug.Assert(localEnd > localStart); 

            // Create and format line
            Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
            Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); 

            if(IsOptimalParagraph) 
            { 
                ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst;
            } 

            TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst);

            // Assert that number of characters in Text line is the same as our expected length 
            Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]");
 
            // Get rectangles from start and end positions of range 
            rectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(lineDesc.urStart), TextDpi.FromTextDpi(lineDesc.vrStart));
 
            // Rectangles must have at least one element
            Invariant.Assert(rectangles.Count > 0);

            // Dispose the line 
            line.Dispose();
 
            return rectangles; 
        }
 
        // ------------------------------------------------------------------
        // Hittest composite lines.
        // -----------------------------------------------------------------
        // ----------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical functions PtsHelper.LineListCompositeFromTextPara 
        ///            and PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical] 
        private IInputElement InputHitTestCompositeLines(
            PTS.FSPOINT pt,
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);
 
            IInputElement ie = null; 
            int cpTextParaStart = Paragraph.ParagraphStartCharacterPosition;
 
            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc;
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected composite line by looking at vertical offset
            for (int index = 0; index < arrayLineDesc.Length; index++) 
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) continue; 

                if (lineDesc.vrStart + lineDesc.dvrAscent + lineDesc.dvrDescent > pt.v)
                {
                    // Affected composite line has been found. 

                    // Get list of line elements. 
                    PTS.FSLINEELEMENT [] arrayLineElement; 
                    PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
                    // increment is unreachable (loop exits after first iteration) because of 'break' - bug 1788192
                    #pragma warning disable 0162
                    for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
                    { 
                        PTS.FSLINEELEMENT element = arrayLineElement[elIndex];
 
                        // Create and format line 
                        Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache);
                        Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, cpTextParaStart); 

                        if(IsOptimalParagraph)
                        {
                            ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                        }
 
 
                        using (line)
                        { 
                            TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst);

                            // Assert that number of characters in Text line is the same as our expected length
                            Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); 

                            if (element.urStart + line.CalculateUOffsetShift() <= pt.u && pt.u <= (element.urStart + line.CalculateUOffsetShift() + element.dur)) 
                            { 
                                int distance = pt.u - element.urStart;
 
                                // Assert that number of characters in Text line is the same as our expected length
                                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                                // Assert that number of characters in Text line is the same as our expected length 
                                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");
 
                                if ((line.Start <= distance) && (distance <= (line.Start + line.Width))) 
                                {
                                    ie = line.InputHitTest(distance); 
                                }
                            }
                        }
                        break; 
                    }
                    #pragma warning restore 0162 
 
                    break;
                } 
            }

            return ie;
        } 

        // ----------------------------------------------------------------- 
        // Get Rectangles in composite lines. 
        // ------------------------------------------------------------------
        ///  
        /// Critical - as this calls the Critical functions PtsHelper.LineListCompositeFromTextPara.
        /// 
        [SecurityCritical]
        private List GetRectanglesInCompositeLines( 
            ContentElement e,
            int start, 
            int length, 
            ref PTS.FSTEXTDETAILSFULL textDetails)
        { 
            ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap);

            List rectangles = new List();
            int localStart = start - Paragraph.ParagraphStartCharacterPosition; 

            if (localStart < 0) 
            { 
                // May happen in case of figures, floaters
                return rectangles; 
            }

            // Get list of complex lines.
            PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; 
            PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc);
 
            // Find affected composite line by looking at vertical offset 
            for (int index = 0; index < arrayLineDesc.Length; index++)
            { 
                PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index];
                if (lineDesc.cElements == 0) continue;

                List lineRectangles = GetRectanglesInCompositeLine(lineDesc, e, localStart, length); 
                Invariant.Assert(lineRectangles != null);
 
                if (lineRectangles.Count != 0) 
                {
                    rectangles.AddRange(lineRectangles); 
                }
            }

            return rectangles; 
        }
 
        // ----------------------------------------------------------------- 
        // Returns ArrayList of rectangles (at msot one) for the given ContentElement
        // e if it spans all or part of the specified line 
        // ------------------------------------------------------------------
        // ------------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical function PtsHelper.LineElementListFromCompositeLine. 
        /// 
        [SecurityCritical] 
        private List GetRectanglesInCompositeLine( 
            PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc,
            ContentElement e, 
            int start,
            int length)
        {
            List rectangles = new List(); 
            int end = start + length;
 
            // If the element does not lie in the line at all, return empty list 
            //if (start > lineDesc.dcpLim)
            //{ 
            //    // Element starts after line ends
            //    return rectangles;
            //}
            //if (end < lineDesc.dcpFirst) 
            //{
                // Element ends before line starts 
            //    return rectangles; 
            //}
 
            // Get list of line elements.
            PTS.FSLINEELEMENT[] arrayLineElement;
            PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement);
 
            for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++)
            { 
                PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; 

                // Check if element we are looking for does not span the current element at all 
                if (start >= element.dcpLim)
                {
                    // Element starts after other element ends
                    continue; 
                }
                if (end <= element.dcpFirst) 
                { 
                    // Element ends before line starts
                    continue; 
                }
                // Establish start and end points of element span within the line so that
                // we can get rectangle between them
                int localStart = (start < element.dcpFirst) ? element.dcpFirst : start; 
                int localEnd = (end < element.dcpLim) ? end : element.dcpLim;
                Debug.Assert(localEnd > localStart); 
 
                Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition);
                Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); 

                if(IsOptimalParagraph)
                {
                    ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; 
                }
 
                TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); 

                // Assert that number of characters in Text line is the same as our expected length 
                Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]");

                // Get rectangles from start and end positions of range for this element
                List elementRectangles = line.GetRangeBounds(localStart, localEnd - localStart, TextDpi.FromTextDpi(element.urStart), TextDpi.FromTextDpi(lineDesc.vrStart)); 

                // Rectangles must have at least one element 
                Invariant.Assert(elementRectangles.Count > 0); 

                // Add rectangles from this element to rectangles from whole line 
                rectangles.AddRange(elementRectangles);

                // Dispose the line
                line.Dispose(); 
            }
            return rectangles; 
        } 

        #endregion Private Methods 

        #region Private Properties

        private bool IsOptimalParagraph { get { return TextParagraph.IsOptimalParagraph; } } 

        #endregion 
 
        //-------------------------------------------------------------------
        // 
        //  Private Fields
        //
        //--------------------------------------------------------------------
 
        #region Private Fields
 
        private int _lineIndexFirstVisual = -1; 

        #endregion Private Fields 
    }
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK