TableParaClient.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / PtsHost / TableParaClient.cs / 1 / TableParaClient.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: TableParaClient holds display related information. 
//
// History: 
//  05/19/2003 : olego - Created
//
//---------------------------------------------------------------------------
 
using MS.Internal;
using MS.Internal.PtsTable; 
using MS.Internal.Text; 
using MS.Internal.Documents;
using System; 
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Security;
using System.Windows; 
using System.Windows.Documents; 
using System.Windows.Media;
 
using MS.Internal.PtsHost.UnsafeNativeMethods;

namespace MS.Internal.PtsHost
{ 
    /// 
    /// Table display data 
    ///  
    internal sealed class TableParaClient : BaseParaClient
    { 
        //-----------------------------------------------------
        //
        //  Constructors
        // 
        //-----------------------------------------------------
 
        #region Constructors 

        ///  
        /// Constructor
        /// 
        /// Owner of the para client
        internal TableParaClient(TableParagraph paragraph) : base(paragraph) 
        {
        } 
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Protected Methods
        // 
        //-----------------------------------------------------
 
        #region Protected Methods 

        ///  
        /// Arrange table paragraph by calling Arrange on all cells
        /// 
        /// 
        /// Critical, because: 
        ///     a) calls Critical function QueryRowDetails.
        ///     b) calls Critical function PTS.FsTransformRectangle 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnArrange()
        {
            Debug.Assert(   Table != null 
                        &&  CalculatedColumns != null  );
 
            base.OnArrange(); 

            _columnRect = Paragraph.StructuralCache.CurrentArrangeContext.ColumnRect; 

            CalculatedColumn[] calculatedColumns = CalculatedColumns;
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rect;
 
            if (    //  table has no rows thus no cells to arrange 
                    !QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect)
                    //  no changes requiring arrange in the table 
                ||  fskupdTable == PTS.FSKUPDATE.fskupdNoChange
                ||  fskupdTable == PTS.FSKUPDATE.fskupdShifted  )
            {
                return; 
            }
 
            // Rect may have moved from collision with figure or floater. 
            _rect = rect;
 
            // Update chunk information
            UpdateChunkInfo(arrayTableRowDesc);

            MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element); 

            if(ParentFlowDirection != PageFlowDirection) 
            { 
                PTS.FSRECT pageRect = _pageContext.PageRect;
 
                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ParentFlowDirection), ref pageRect, ref _rect, PTS.FlowDirectionToFswdir(PageFlowDirection), out _rect));
                mbp.MirrorMargin();
            }
 
            _rect.u += mbp.MarginLeft;
            _rect.du -= mbp.MarginLeft + mbp.MarginRight; 
 
            int vrRowTop = GetTableOffsetFirstRowTop() + TextDpi.ToTextDpi(Table.InternalCellSpacing) / 2;
 
            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
            {
                PTS.FSKUPDATE fskupdRow;
                PTS.FSKUPDATE[] arrayUpdate; 
                IntPtr[] arrayFsCell;
                PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
 
                fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited)
                    ? arrayTableRowDesc[iR].fsupdinf.fskupd 
                    : fskupdTable;

                if (fskupdRow == PTS.FSKUPDATE.fskupdNoChange)
                { 
                    //  no changes requiring arrange in the row
                    vrRowTop += arrayTableRowDesc[iR].u.dvrRow; 
                    continue; 
                }
 
                QueryRowDetails(
                    arrayTableRowDesc[iR].pfstablerow,
                    out arrayFsCell,
                    out arrayUpdate, 
                    out arrayTableCellMerge);
 
                for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                {
                    if (arrayFsCell[iC] == IntPtr.Zero) 
                    {
                        //  paginated case - cell may be null
                        continue;
                    } 

                    if (    iR != 0 //  paginated case - row spanned cells appearing in first row on the page must be accounted 
                        &&  (   arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle 
                            ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast  )   )
                    { 
                        // this cell has been accounted
                        continue;
                    }
 
                    CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
 
                    double urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset; 

                    cellParaClient.Arrange(TextDpi.ToTextDpi(urCellOffset), vrRowTop, _rect, ThisFlowDirection, _pageContext); 

                }

                vrRowTop += arrayTableRowDesc[iR].u.dvrRow; 

                if (iR == 0 && IsFirstChunk) 
                { 
                    // Remove extra spacing we reported to PTS in RowProperties for top row in table
                    vrRowTop -= mbp.BPTop; 
                }
            }
        }
 

        #endregion Protected Methods 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        ///  
        /// Validate table visual
        ///  
        /// PTS update info
        /// 
        /// Critical - as this calls Critical function ValidateRowVisualComplex and
        ///            ValidateRowVisualSimple. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) 
        {
            Invariant.Assert( fskupdInherited != PTS.FSKUPDATE.fskupdInherited );
            Invariant.Assert( TableParagraph.Table != null && CalculatedColumns != null );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rect; 
            Table table = TableParagraph.Table;
 
            Visual.Clip = new RectangleGeometry(_columnRect.FromTextDpi());

            if (!QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect))
            { 
                //  table has no rows thus no cell to validate
                _visual.Children.Clear(); 
                return; 
            }
 
            MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element);
            if (ThisFlowDirection != PageFlowDirection)
            {
                mbpInfo.MirrorBP(); 
            }
 
            if (fskupdTable == PTS.FSKUPDATE.fskupdInherited) 
            {
                fskupdTable = fskupdInherited; 
            }

            if (fskupdTable == PTS.FSKUPDATE.fskupdNoChange)
            { 
                //  no need to arrange because nothing changed
                return; 
            } 

            if (fskupdTable == PTS.FSKUPDATE.fskupdShifted) 
            {
                fskupdTable = PTS.FSKUPDATE.fskupdNew;
            }
 
            VisualCollection rowVisualsCollection = _visual.Children;
            if (fskupdTable == PTS.FSKUPDATE.fskupdNew) 
            { 
                rowVisualsCollection.Clear();
            } 

            // Draw border and background info.
            Brush backgroundBrush = (Brush)Paragraph.Element.GetValue(TextElement.BackgroundProperty);
 
            using (DrawingContext dc = _visual.RenderOpen())
            { 
                Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi(); 

                _visual.DrawBackgroundAndBorderIntoContext(dc, backgroundBrush, mbpInfo.BorderBrush, mbpInfo.Border, _rect.FromTextDpi(), IsFirstChunk, IsLastChunk); 

                DrawColumnBackgrounds(dc, tableContentRect);
                DrawRowGroupBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo);
                DrawRowBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo); 
            }
 
            TableRow rowPrevious = null; 

            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
            {
                PTS.FSKUPDATE fskupdRow;
                RowParagraph rowParagraph;
                TableRow row; 

                fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited) 
                    ? arrayTableRowDesc[iR].fsupdinf.fskupd 
                    : fskupdTable;
 

                rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow));
                row = rowParagraph.Row;
 
                //
                //  STEP 1 SYNCHRONIZATION 
                //  --------------------------------------------------------- 
                //  synchronize rowVisualCollection.
                //  for newly created rows visual is inserted; 
                //  otherwise for removed rows (if any) corresponding visuals are removed
                //
                if (fskupdRow == PTS.FSKUPDATE.fskupdNew)
                { 
                    RowVisual rowVisual = new RowVisual(row);
                    rowVisualsCollection.Insert(iR, rowVisual); 
                } 
                else
                { 
                    SynchronizeRowVisualsCollection(rowVisualsCollection, iR, row);
                }

                Invariant.Assert(((RowVisual)rowVisualsCollection[iR]).Row == row); 

                // 
                //  STEP 2 CELL VISUALS VALIDATION 
                //  ----------------------------------------------------------
                //  for new or changed rows go inside and validate cells 
                //
                if (    fskupdRow == PTS.FSKUPDATE.fskupdNew
                    ||  fskupdRow == PTS.FSKUPDATE.fskupdChangeInside   )
                { 
                    // paginated case - if first row of a given rowgroup for this para client has foreign cells, they need to
                    // be rendered regardless of merge state 
                    if(rowParagraph.Row.HasForeignCells && (rowPrevious == null || rowPrevious.RowGroup != row.RowGroup)) 
                    {
                        ValidateRowVisualComplex( 
                            (RowVisual)(rowVisualsCollection[iR]),
                            arrayTableRowDesc[iR].pfstablerow,
                            CalculatedColumns.Length,
                            fskupdRow, 
                            CalculatedColumns);
                    } 
                    else 
                    {
                        ValidateRowVisualSimple( 
                            (RowVisual)(rowVisualsCollection[iR]),
                            arrayTableRowDesc[iR].pfstablerow,
                            fskupdRow,
                            CalculatedColumns); 
                    }
                } 
 
                rowPrevious = row;
            } 

            //
            //  STEP 4
 

            if (rowVisualsCollection.Count > arrayTableRowDesc.Length) 
            { 
                rowVisualsCollection.RemoveRange(
                    arrayTableRowDesc.Length, 
                    rowVisualsCollection.Count - arrayTableRowDesc.Length);
            }
        }
 
        /// ------------------
        /// Updates viewport 
        /// -------------------- 
        /// 
        /// Critical - as this calls Critical functions QueryRowDetails. 
        /// Safe - The arrayTableRowDesc parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateViewport(ref PTS.FSRECT viewport) 
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  ); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                { 
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;

                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow, 
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge); 

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        {
                            //  paginated case - cell may be null 
                            continue;
                        } 
 
                        CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        cellParaClient.UpdateViewport(ref viewport); 
                    }
                }
            }
        } 

        ///  
        /// Performs hit testing. 
        /// 
        /// Point of interest. 
        /// Element if any.
        /// 
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal override IInputElement InputHitTest(PTS.FSPOINT pt)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  );

            IInputElement element = null; 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                // Start vrRowTop from 'true' v of table
                int vrRowTop = GetTableOffsetFirstRowTop() + rectTable.v;
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    if (pt.v >= vrRowTop && pt.v <= (vrRowTop + arrayTableRowDesc[iR].u.dvrRow)) 
                    { 
                        PTS.FSKUPDATE[] arrayUpdate;
                        IntPtr[] arrayFsCell; 
                        PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;

                        QueryRowDetails(
                            arrayTableRowDesc[iR].pfstablerow, 
                            out arrayFsCell,
                            out arrayUpdate, 
                            out arrayTableCellMerge); 

                        for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                        {
                            if (arrayFsCell[iC] == IntPtr.Zero)
                            {
                                //  paginated case - cell may be null 
                                continue;
                            } 
 
                            CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                            PTS.FSRECT rect = cellParaClient.Rect; 

                            if (cellParaClient.Rect.Contains(pt))
                            {
                                element = cellParaClient.InputHitTest(pt); 
 								break;
                            } 
                        } 
                        break;
                    } 
                    vrRowTop += arrayTableRowDesc[iR].u.dvrRow;
                }
            }
 
            if(element == null && _rect.Contains(pt))
            { 
                element = TableParagraph.Table; 
            }
 
            return (element);
        }

        ///  
        /// Returns ArrayList of rectangles for element if found.
        ///  
        ///  
        /// ContentElement for which rectangles are to be found.
        ///  
        /// 
        /// int representing start offset of e
        /// 
        ///  
        /// int representing number of positions occupied by element.
        ///  
        ///  
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override List GetRectangles(ContentElement e, int start, int length) 
        {
            Debug.Assert(TableParagraph.Table != null 
                        && CalculatedColumns != null); 
            List rectangles = new List();
 
            if (TableParagraph.Table == e)
            {
                // We have found the element. Return rectangles for this paragraph.
                GetRectanglesForParagraphElement(out rectangles); 
            }
            else 
            { 
                // Search subpage for table cells
                PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
                PTS.FSKUPDATE fskupdTable;
                PTS.FSRECT rectTable;

                rectangles = new List(); 
                if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
                { 
                    for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                    {
                        PTS.FSKUPDATE[] arrayUpdate; 
                        IntPtr[] arrayFsCell;
                        PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
                        QueryRowDetails(
                                arrayTableRowDesc[iR].pfstablerow, 
                                out arrayFsCell,
                                out arrayUpdate, 
                                out arrayTableCellMerge); 
                        for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                        { 
                            if (arrayFsCell[iC] == IntPtr.Zero)
                            {
                                //  paginated case - cell may be null
                                continue; 
                            }
 
                            CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                            if (start < cellParaClient.Paragraph.ParagraphEndCharacterPosition)
                            { 
                                // Element start is within cell boundary
                                rectangles = cellParaClient.GetRectangles(e, start, length);
                                Invariant.Assert(rectangles != null);
                                if (rectangles.Count != 0) 
                                {
                                    break; 
                                } 
                            }
                        } 
                        if (rectangles.Count != 0)
                        {
                            break;
                        } 
                    }
                } 
            } 

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

        ///  
        /// Paragraph results - verification
        ///  
        internal override ParagraphResult CreateParagraphResult() 
        {
            return new TableParagraphResult(this); 
        }

        /// 
        /// Return TextContentRange for the content of the paragraph. 
        /// 
        ///  
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override TextContentRange GetTextContentRange()
        { 
            TextContentRange range = null;
 
            // Search subpage for table cells 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable;
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                range = new TextContentRange(); 
                UpdateChunkInfo(arrayTableRowDesc);
                TextElement elementOwner = this.Paragraph.Element as TextElement; 
 
                // Add range for Table leading edge, if necessary
                if (_isFirstChunk) 
                    range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                        elementOwner, ElementEdge.BeforeStart));

                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    PTS.FSTABLEROWDETAILS tableRowDetails; 
                    TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; 
                    PTS.Validate(PTS.FsQueryTableObjRowDetails(
                        PtsContext.Context, 
                        arrayTableRowDesc[iR].pfstablerow,
                        out tableRowDetails));

                    if (tableRowDetails.fskboundaryAbove != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) 
                    {
                        // Add range for TableRowGroup leading edge, if necessary 
                        if (row.Index == 0) 
                            range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                                row.RowGroup, ElementEdge.BeforeStart)); 

                        range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                            row, ElementEdge.BeforeStart));
                    } 

                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
                    QueryRowDetails( 
                            arrayTableRowDesc[iR].pfstablerow,
                            out arrayFsCell,
                            out arrayUpdate,
                            out arrayTableCellMerge); 

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    { 
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        { 
                            //  paginated case - cell may be null
                            continue;
                        }
 
                        CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        range.Merge(cellParaClient.GetTextContentRange()); 
                    } 

                    if (tableRowDetails.fskboundaryBelow != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) 
                    {
                        range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                            row, ElementEdge.AfterEnd));
 
                        // Add range for TableRowGroup trailing edge, if necessary
                        if (row.Index == row.RowGroup.Rows.Count - 1) 
                            range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( 
                                row.RowGroup, ElementEdge.AfterEnd));
                    } 
                }

                // Add range for Table trailing edge, if necessary
                if (_isLastChunk) 
                    range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                        elementOwner, ElementEdge.AfterEnd)); 
            } 

            if (range == null) 
            {
                // if table has no rows or no cells
                range = TextContainerHelper.GetTextContentRangeForTextElement(TableParagraph.Table);
            } 

            return range; 
        } 

        ///  
        /// Returns the paragraphs for the appropriate cell, given a point
        /// 
        /// Point to check for cell.
        /// Whether to snap to text 
        /// 
        /// CellParaClient found 
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellParaClientFromPoint(Point point, bool snapToText)
        { 
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
            int u = TextDpi.ToTextDpi(point.X); 
            int v = TextDpi.ToTextDpi(point.Y);
 
            CellParaClient cpcFound = null; 
            CellParaClient cpcClosest = null;
            int iClosestDistance = int.MaxValue; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length && cpcFound == null; ++iR) 
                {
                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge);
 
                    for (int iC = 0; iC < arrayFsCell.Length && cpcFound == null; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                        PTS.FSRECT rect = cpc.Rect; 

                        if (u >= rect.u && u <= (rect.u + rect.du) && 
                            v >= rect.v && v <= (rect.v + rect.dv))
                        {
                            cpcFound = cpc;
                        } 
                        else if(snapToText)
                        { 
                            int du = Math.Min(Math.Abs(rect.u - u), Math.Abs(rect.u + rect.du - u)); 
                            int dv = Math.Min(Math.Abs(rect.v - v), Math.Abs(rect.v + rect.dv - v));
 
                            if( (du + dv) < iClosestDistance)
                            {
                                iClosestDistance = du + dv;
                                cpcClosest = cpc; 
                            }
                        } 
                    } 
                }
            } 

            if(snapToText && cpcFound == null)
            {
                cpcFound = cpcClosest; 
            }
 
            return cpcFound; 
        }
 


        /// 
        /// Returns the paragraphs for all rows in a table 
        /// 
        ///  
        /// True if any child paragraph result has text content 
        /// 
        ///  
        /// Array of paragraph results
        /// 
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetChildrenParagraphResults(out bool hasTextContent) 
        {
            MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element);
            if (ThisFlowDirection != PageFlowDirection)
            { 
                mbpInfo.MirrorBP();
            } 
 
            Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi();
            double rowTop = tableContentRect.Y; 
            Rect rowRect = tableContentRect;
            hasTextContent = false;

            List rowParagraphResults = new List(0); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable;
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                {
                    RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow)); 

                    rowRect.Y = rowTop; 
                    rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); 
                    RowParagraphResult rowParagraphResult = new RowParagraphResult(this, iR, rowRect, rowParagraph);
                    if (rowParagraphResult.HasTextContent) 
                    {
                        hasTextContent = true;
                    }
                    rowParagraphResults.Add(rowParagraphResult); 

                    rowTop += rowRect.Height; // Adjust for top of next row 
                } 
            }
 
            return new ReadOnlyCollection(rowParagraphResults);
        }

        ///  
        /// Returns the paragraphs for a given row
        ///  
        /// Row index 
        /// 
        /// Array of paragraph results 
        /// 
        /// 
        /// True if any child paragraph has text content
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetChildrenParagraphResultsForRow(int rowIndex, out bool hasTextContent)
        {
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
 
            List cellParagraphResults = new List(0);
            hasTextContent = false; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                PTS.FSKUPDATE[] arrayUpdate; 
                IntPtr[] arrayFsCell;
                PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
 
                QueryRowDetails(
                    arrayTableRowDesc[rowIndex].pfstablerow, 
                    out arrayFsCell,
                    out arrayUpdate,
                    out arrayTableCellMerge);
 
                for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                { 
                    if(arrayFsCell[iC] != IntPtr.Zero && 
                        (rowIndex == 0 ||
                          (arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle && 
                           arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast)
                        )
                      )
                    { 
                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        ParagraphResult cellParagraphResult = cpc.CreateParagraphResult(); 
                        if (cellParagraphResult.HasTextContent) 
                        {
                            hasTextContent = true; 
                        }
                        cellParagraphResults.Add(cellParagraphResult);
                    }
                } 
            }
 
            return new ReadOnlyCollection(cellParagraphResults); 
        }
 
        /// 
        /// Returns the paragraphs for the appropriate cell, given a point
        /// 
        /// Point to check for cell. 
        /// Whether to snap to text
        ///  
        /// Array of paragraph results 
        /// 
        internal ReadOnlyCollection GetParagraphsFromPoint(Point point, bool snapToText) 
        {
            CellParaClient cpc = GetCellParaClientFromPoint(point, snapToText);

            List listResults = new List(0); 

            if(cpc != null) 
            { 
                listResults.Add(cpc.CreateParagraphResult());
            } 

            return new ReadOnlyCollection(listResults);
        }
 
        /// 
        /// Returns the paragraphs for the appropriate cell, given a point 
        ///  
        /// Position of cell.
        ///  
        /// Array of paragraph results
        /// 
        internal ReadOnlyCollection GetParagraphsFromPosition(ITextPointer position)
        { 
            CellParaClient cpc = GetCellParaClientFromPosition(position);
 
            List listResults = new List(0); 

            if(cpc != null) 
            {
                listResults.Add(cpc.CreateParagraphResult());
            }
 
            return new ReadOnlyCollection(listResults);
        } 
 
        /// 
        /// Returns tight bounding path geometry. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  ); 
 
            Geometry geometry = null;
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
 
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                bool passedEndPosition = false; 

                for (int iR = 0; iR < arrayTableRowDesc.Length && !passedEndPosition; ++iR) 
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell;
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 

                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow, 
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge);

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                    { 
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        { 
                            //  paginated case - cell may be null 
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));

                        if (endPosition.CompareTo(cpc.Cell.ContentStart) <= 0) 
                        {
                            //  remember that at least one cell in this row starts after the range's end. 
                            //  in this case it is safe to break after this whole row is processed. 
                            //  Note: can not break right away because cells in arrayFsCell come not
                            //  necessarily in backing store (cp) order. 
                            passedEndPosition = true;
                        }
                        else
                        { 
                            if (startPosition.CompareTo(cpc.Cell.ContentEnd) <= 0)
                            { 
                                Geometry cellGeometry = cpc.GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, visibleRect); 
                                CaretElement.AddGeometry(ref geometry, cellGeometry);
                            } 
                        }
                    }
                }
            } 

            if (geometry != null) 
            { 
                geometry = Geometry.Combine(geometry, Visual.Clip, GeometryCombineMode.Intersect, null);
            } 
            return geometry;
        }

        ///  
        /// Returns the para client for a cell, given a position
        ///  
        /// Position to check for cell. 
        /// 
        /// Cell para client 
        /// 
        /// 
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellParaClientFromPosition(ITextPointer position)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  );

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
 
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge);

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        { 
                            //  paginated case - cell may be null
                            continue; 
                        }

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
 
                        if(position.CompareTo(cpc.Cell.ContentStart) >= 0 && position.CompareTo(cpc.Cell.ContentEnd) <= 0)
                        { 
                            return cpc; 
                        }
                    } 
                }
            }

            return (null); 
        }
 
 
        /// 
        /// Returns an appropriate found cell 
        /// 
        /// Suggested X position for cell to find.
        /// RowGroupIndex to be above.
        /// RowIndex to be above. 
        /// 
        /// Cell Para Client of cell 
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellAbove(double suggestedX, int rowGroupIndex, int rowIndex)
        { 
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
            int suggestedU = TextDpi.ToTextDpi(suggestedX); 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            { 
                MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element);
 
                for (int iR = arrayTableRowDesc.Length - 1; iR >= 0; --iR)
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge);

                    CellParaClient cpcClosest = null; 
                    int iClosestDistance = int.MaxValue;
 
                    for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
 
                        // Check if bottom row of cell is less than row index for moving up.
 
                        int cellBottomIndex = cpc.Cell.RowIndex + cpc.Cell.RowSpan - 1;
                        if( (cellBottomIndex < rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) ||
                            (cpc.Cell.RowGroupIndex < rowGroupIndex)
                          ) 
                        {
                            if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) 
                            { 
                                return cpc;
                            } 
                            // Else record it, and return it if we find nothing better for this row.

                            int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU);
 
                            if(iDistance < iClosestDistance)
                            { 
                                iClosestDistance = iDistance; 
                                cpcClosest = cpc;
                            } 
                        }
                    }

                    if(cpcClosest != null) 
                    {
                        return cpcClosest; 
                    } 
                }
            } 

            return (null);
        }
 
        /// 
        /// Returns an appropriate found cell 
        ///  
        /// Suggested X position for cell to find.
        /// RowGroupIndex to be above. 
        /// RowIndex to be below.
        /// 
        /// Cell Para Client of cell
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal CellParaClient GetCellBelow(double suggestedX, int rowGroupIndex, int rowIndex)
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
            int suggestedU = TextDpi.ToTextDpi(suggestedX);

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                { 
                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell;
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 

                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge); 
 
                    CellParaClient cpcClosest = null;
                    int iClosestDistance = int.MaxValue; 

                    for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC)
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null 
                            continue; 
                        }
 
                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));

                        if( (cpc.Cell.RowIndex > rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) ||
                            (cpc.Cell.RowGroupIndex > rowGroupIndex) 
                            )
                        { 
                            if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) 
                            {
                                return cpc; 
                            }

                            // Else record it and report it if we find nothing better in this row.
                            int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU); 

                            if(iDistance < iClosestDistance) 
                            { 
                                iClosestDistance = iDistance;
                                cpcClosest = cpc; 
                            }
                        }

                    } 

                    if(cpcClosest != null) 
                    { 
                        return cpcClosest;
                    } 
                }
            }

            return (null); 
        }
 
 
        /// 
        /// Returns a cellinfo structure for a given point 
        /// 
        /// Point to check for cell.
        /// 
        /// CellInfo class 
        /// 
        internal CellInfo GetCellInfoFromPoint(Point point) 
        { 
            CellParaClient cpc = GetCellParaClientFromPoint(point, true);
 
            if(cpc != null)
            {
                return new CellInfo(this, cpc);
            } 

            return null; 
        } 

        ///  
        /// Returns a rect corresponding to a row end position.
        /// 
        /// Position to check against.
        ///  
        /// Rect, area
        ///  
        ///  
        /// Critical - as this uses PTS.FSTABLEROWDESCRIPTION, a structure with
        ///         overlapping fields (to match a union in PTS) 
        /// Safe - returned information is safe to disclose.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Rect GetRectangleFromRowEndPosition(ITextPointer position) 
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  ); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                int vrCur = GetTableOffsetFirstRowTop() + rectTable.v; 
 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                { 
                    TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row;

                    if(((TextPointer)position).CompareTo(row.ContentEnd) == 0)
                    { 
                        return new Rect( TextDpi.FromTextDpi(rectTable.u + rectTable.du),
                                         TextDpi.FromTextDpi(vrCur), 
                                         1.0, 
                                         TextDpi.FromTextDpi(arrayTableRowDesc[iR].u.dvrRow));
                    } 

                    vrCur += arrayTableRowDesc[iR].u.dvrRow;
                }
            } 

            // Valid row end position should have been specified. 
            Debug.Assert(false); 

            return System.Windows.Rect.Empty; 
        }


 
        /// 
        /// AutofitTable 
        ///  
        /// Direction of Track
        /// Available space 
        /// Table width after autofit. It is the same for all rows
        /// 
        /// durAvailableSpace is invalid due to the unfinished table/footer work in
        /// TableSection.GetPageDimensions (see notes in that method for more info). 
        /// 
        internal void AutofitTable( 
            uint fswdirTrack,                       // IN:  direction of Track 
            int durAvailableSpace,                  // IN:  available space (?)
            out int durTableWidth)                  // OUT: Table width after autofit. It is the same for all rows :) 
        {
            Debug.Assert(Table != null);

            double availableSpace = TextDpi.FromTextDpi(durAvailableSpace); 
            double tableWidth;
 
            Autofit(availableSpace, out tableWidth); 
            durTableWidth = TextDpi.ToTextDpi(tableWidth);
        } 

        /// 
        /// UpdAutofitTable
        ///  
        /// Direction of Track
        /// Available space 
        /// Table width after autofit 
        /// Column width changes
        ///  
        /// durAvailableSpace is invalid due to the unfinished table/footer work in
        /// TableSection.GetPageDimensions (see notes in that method for more info).
        /// 
        internal void UpdAutofitTable( 
            uint fswdirTrack,                       // IN:  direction of Track
            int durAvailableSpace,                  // IN:  available space (?) 
            out int durTableWidth,                  // OUT: Table width after autofit. 
            out int fNoChangeInCellWidths)          // OUT:
        { 
            Debug.Assert(Table != null);

            double availableSpace = TextDpi.FromTextDpi(durAvailableSpace);
            double tableWidth; 
            fNoChangeInCellWidths = Autofit(availableSpace, out tableWidth);
            durTableWidth = TextDpi.ToTextDpi(tableWidth); 
        } 

 
        /// 
        /// Performs autofit calculations
        /// 
        /// Available width for table 
        /// Resulting table width
        ///  
        /// PTS.True when no changes in columns width detected, 
        /// PTS.False otherwise
        ///  
        internal int Autofit(double availableWidth, out double tableWidth)
        {
            int ret = PTS.True;
 
            ValidateCalculatedColumns();
 
            if (!DoubleUtil.AreClose(availableWidth, _previousAutofitWidth)) 
            {
                ret = ValidateTableWidths(availableWidth, out tableWidth); 
            }
            else
            {
                tableWidth = _previousTableWidth; 
            }
 
            _previousAutofitWidth = availableWidth; 
            _previousTableWidth = tableWidth;
 
            return ret;
        }

 
        #endregion Internal Methods
 
        //----------------------------------------------------- 
        //
        //  Internal Properties 
        //
        //-----------------------------------------------------

        #region Internal Properties 

        ///  
        /// Table paragraph created the client 
        /// 
        internal TableParagraph TableParagraph 
        {
            get { return (TableParagraph)_paragraph; }
        }
 
        /// 
        /// Table object proper 
        ///  
        internal Table Table
        { 
            get { return TableParagraph.Table; }
        }

 
        /// 
        /// CurrentPage Desired Width 
        ///  
        internal double TableDesiredWidth
        { 
            get
            {
                // Use current format context's page width to limit cell spacing
                double durRet = 0; 

                CalculatedColumn[] cols = CalculatedColumns; 
 
                for (int i = 0; i < cols.Length; i++)
                { 
                    durRet += cols[i].DurWidth + Table.InternalCellSpacing;
                }

                return durRet; 
            }
        } 
 
        /// 
        /// CalculatedColumns 
        /// 
        internal CalculatedColumn[] CalculatedColumns
        {
            get 
            {
                return (_calculatedColumns); 
            } 
        }
 
        /// 
        /// Autofit width - what width we used when autofitting the table.
        /// 
        internal double AutofitWidth 
        {
            get { return _previousAutofitWidth; } 
        } 

        // ------------------------------------------------------------------ 
        // Is this the first chunk of paginated content.
        // -----------------------------------------------------------------
        internal override bool IsFirstChunk { get { return _isFirstChunk; } }
        private bool _isFirstChunk; 

        // ------------------------------------------------------------------ 
        // Is this the last chunk of paginated content. 
        // ------------------------------------------------------------------
        internal override bool IsLastChunk { get { return _isLastChunk; } } 
        private bool _isLastChunk;

        #endregion Internal Properties
 
        //-----------------------------------------------------
        // 
        //  Private Methods 
        //
        //------------------------------------------------------ 

        #region Private Methods

        // ----------------------------------------------------------------- 
        // Update information about first/last chunk.
        // ----------------------------------------------------------------- 
        ///  
        /// Queries PTS and saves information about this paginated chunk
        ///  
        /// Table Row descriptions
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTableObjRowDetails
        ///  
        [SecurityCritical]
        private unsafe void UpdateChunkInfo(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc) 
        { 
            PTS.FSTABLEROWDETAILS tableRowDetails;
 
            RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow));
            TableRow row = rowParagraph.Row;

            PTS.Validate(PTS.FsQueryTableObjRowDetails( 
                PtsContext.Context,
                arrayTableRowDesc[0].pfstablerow, 
                out tableRowDetails)); 

            _isFirstChunk = (tableRowDetails.fskboundaryAbove == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) && 
                (row.Index == 0) && Table.IsFirstNonEmptyRowGroup(row.RowGroup.Index);

            row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[arrayTableRowDesc.Length - 1].fsnmRow))).Row;
 
            PTS.Validate(PTS.FsQueryTableObjRowDetails(
                PtsContext.Context, 
                arrayTableRowDesc[arrayTableRowDesc.Length - 1].pfstablerow, 
                out tableRowDetails));
 
            _isLastChunk = (tableRowDetails.fskboundaryBelow == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) &&
                (row.Index == row.RowGroup.Rows.Count - 1) && Table.IsLastNonEmptyRowGroup(row.RowGroup.Index);
        }
 

        ///  
        /// Queries PTS and returns table detailed structure / layout information about rows. 
        /// 
        /// Storage for table rows information 
        /// Result of update
        /// Rect of the table
        /// true when table has content; false otherwise
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTableObjDetails,
        ///            PTS.FsQueryTableObjTableProperDetails and PTS.FsQueryTableObjRowList. 
        /// Safe - returned information is safe to disclose. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private unsafe bool QueryTableDetails(
            out PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc,
            out PTS.FSKUPDATE fskupdTable,
            out PTS.FSRECT rect) 
        {
            PTS.FSTABLEOBJDETAILS tableObjDetails; 
            PTS.FSTABLEDETAILS tableDetails; 

            PTS.Validate(PTS.FsQueryTableObjDetails( 
                PtsContext.Context,
                _paraHandle.Value,
                out tableObjDetails));
            Debug.Assert(TableParagraph == (TableParagraph)(PtsContext.HandleToObject(tableObjDetails.fsnmTable))); 

            fskupdTable = tableObjDetails.fskupdTableProper; 
            rect = tableObjDetails.fsrcTableObj; 

            PTS.Validate(PTS.FsQueryTableObjTableProperDetails( 
                PtsContext.Context,
                tableObjDetails.pfstableProper,
                out tableDetails));
 
            if (tableDetails.cRows == 0)
            { 
                //  table has no rows (thus no content) 
                arrayTableRowDesc = null;
                return (false); 
            }

            arrayTableRowDesc = new PTS.FSTABLEROWDESCRIPTION[tableDetails.cRows];
 
            fixed (PTS.FSTABLEROWDESCRIPTION * rgTableRowDesc = arrayTableRowDesc)
            { 
                int cRowsActual; 

                PTS.Validate(PTS.FsQueryTableObjRowList( 
                    PtsContext.Context,
                    tableObjDetails.pfstableProper,
                    tableDetails.cRows,
                    rgTableRowDesc, 
                    out cRowsActual));
 
                Debug.Assert(tableDetails.cRows == cRowsActual); 
            }
 
            return (true);
        }

        ///  
        /// Queries PTS and returns detailed structure / layout information about cells.
        ///  
        /// Row to query 
        /// Names of the cells
        /// Update state of the cells 
        /// Merge state of the cells
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTableObjRowDetails
        ///            and PTS.FsQueryTableObjCellList. 
        /// 
        [SecurityCritical] 
        private unsafe void QueryRowDetails( 
            IntPtr pfstablerow,
            out IntPtr[] arrayFsCell, 
            out PTS.FSKUPDATE[] arrayUpdate,
            out PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge)
        {
            PTS.FSTABLEROWDETAILS tableRowDetails; 

            PTS.Validate(PTS.FsQueryTableObjRowDetails( 
                PtsContext.Context, 
                pfstablerow,
                out tableRowDetails)); 

            arrayUpdate = new PTS.FSKUPDATE[tableRowDetails.cCells];
            arrayFsCell = new IntPtr[tableRowDetails.cCells];
            arrayTableCellMerge = new PTS.FSTABLEKCELLMERGE[tableRowDetails.cCells]; 

            if (tableRowDetails.cCells > 0) 
            { 
                fixed (PTS.FSKUPDATE * rgUpdate = arrayUpdate)
                { 
                    fixed (IntPtr * rgFsCell = arrayFsCell)
                    {
                        fixed (PTS.FSTABLEKCELLMERGE * rgTableCellMerge = arrayTableCellMerge)
                        { 
                            int cCellsActual;
 
                            PTS.Validate(PTS.FsQueryTableObjCellList( 
                                PtsContext.Context,
                                pfstablerow, 
                                tableRowDetails.cCells,
                                rgUpdate,
                                rgFsCell,
                                rgTableCellMerge, 
                                out cCellsActual));
 
                            Debug.Assert(tableRowDetails.cCells == cCellsActual); 
                        }
                    } 
                }
            }
        }
 
        /// 
        /// SynchronizeRowVisualsCollection. 
        ///  
        /// Collection to synchromize.
        /// Index to start syncronization from. 
        /// Row that still alive.
        private void SynchronizeRowVisualsCollection(
            VisualCollection rowVisualsCollection,
            int firstIndex, 
            TableRow row)
        { 
            //  quick check to see if collection is already synchronized 
            if (((RowVisual)(rowVisualsCollection[firstIndex])).Row != row)
            { 
                int lastIndex = firstIndex;
                int count = rowVisualsCollection.Count;

                while (++lastIndex < count) 
                {
                    RowVisual rowVisual = (RowVisual)(rowVisualsCollection[lastIndex]); 
                    if (rowVisual.Row == row)   break; 
                }
 
                rowVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex);
            }
        }
 
        /// 
        /// SynchronizeCellVisualsCollection. 
        ///  
        /// Collection to synchromize.
        /// Index to start syncronization from. 
        /// Visual that still alive.
        private void SynchronizeCellVisualsCollection(
            VisualCollection cellVisualsCollection,
            int firstIndex, 
            Visual visual)
        { 
            //  quick check to see if collection is already synchronized 
            if ((cellVisualsCollection[firstIndex]) != visual)
            { 
                int lastIndex = firstIndex;
                int count = cellVisualsCollection.Count;

                while (++lastIndex < count) 
                {
                    if (cellVisualsCollection[lastIndex] == visual) break; 
                } 

                cellVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex); 
            }
        }

        ///  
        /// Validates the row's visual.
        ///  
        /// Row Visual to validate. 
        /// Row to query.
        /// Row's change state. 
        /// Columns offsets information.
        /// 
        /// Critical - as this calls Critical function QueryRowDetails and passes
        ///            pfstablerow directly. 
        /// 
        [SecurityCritical] 
        private void ValidateRowVisualSimple( 
            RowVisual rowVisual,
            IntPtr pfstablerow, 
            PTS.FSKUPDATE fskupdRow,
            CalculatedColumn[] calculatedColumns)
        {
            PTS.FSKUPDATE[] arrayUpdate; 
            IntPtr[] arrayFsCell;
            PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
            VisualCollection cellVisualsCollection; 
            int sourceIndex;
 
            QueryRowDetails(
                pfstablerow,
                out arrayFsCell,
                out arrayUpdate, 
                out arrayTableCellMerge);
 
            cellVisualsCollection = rowVisual.Children; 
            sourceIndex = 0;
 
            for (int iC = 0; iC < arrayFsCell.Length; ++iC)
            {
                CellParaClient cellParaClient;
                double urCellOffset; 
                PTS.FSKUPDATE fskupdCell;
 
                if (    //  paginated case - cell may be null 
                        arrayFsCell[iC] == IntPtr.Zero
                        //  exclude hanging cells 
                    ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle
                    ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast   )
                {
                    continue; 
                }
 
                fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited) 
                    ? arrayUpdate[iC]
                    : fskupdRow; 

                if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange)
                {
                    cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                    urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset;
 
                    cellParaClient.ValidateVisual(); 

                    if (    fskupdCell == PTS.FSKUPDATE.fskupdNew 
                        //  PTS bug is a suspect here - this is a temp workaround:
                        ||  VisualTreeHelper.GetParent(cellParaClient.Visual) == null   )
                    {
                        Visual currentParent = VisualTreeHelper.GetParent(cellParaClient.Visual) as Visual; 
                        if(currentParent != null)
                        { 
                            ContainerVisual parent = currentParent as ContainerVisual; 
                            Invariant.Assert(parent != null, "parent should always derives from ContainerVisual");
                            parent.Children.Remove(cellParaClient.Visual); 
                        }
                        cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual);
                    }
                    else 
                    {
                        Debug.Assert(   cellParaClient.Visual != null 
                                    //  If the check below fails, then PTS cheats by reporting "ChangInside" for 
                                    //  a cell that in fact was re-Formatted.
                                    &&  VisualTreeHelper.GetParent(cellParaClient.Visual) != null   ); 

                        SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual);
                    }
                } 
                sourceIndex++;
            } 
 
            if (cellVisualsCollection.Count > sourceIndex)
            { 
                cellVisualsCollection.RemoveRange(
                    sourceIndex,
                    cellVisualsCollection.Count - sourceIndex);
            } 

            #if DEBUGDEBUG 
            for (int iC = 0, sourceIndex = 0; iC < arrayFsCell.Length; ++iC) 
            {
                if (    arrayFsCell[iC] != IntPtr.Zero 
                    &&  arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle
                    &&  arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast   )
                {
                    CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                    Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex);
                    sourceIndex++; 
                } 
            }
            #endif // DEBUGDEBUG 
        }

        /// 
        /// Validates the row's visual. 
        /// 
        /// Row Visual to validate. 
        /// Row to query. 
        /// Number of columns in the table.
        /// Row's change state. 
        /// Columns offsets information.
        /// 
        ///
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails and passes it 
        ///            pfstablerow directly. 
        /// 
        [SecurityCritical] 
        private void ValidateRowVisualComplex(
            RowVisual rowVisual,
            IntPtr pfstablerow,
            int tableColumnCount, 
            PTS.FSKUPDATE fskupdRow,
            CalculatedColumn[] calculatedColumns) 
        { 
            PTS.FSKUPDATE[] arrayUpdate;
            IntPtr[] arrayFsCell; 
            PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
            CellParaClientEntry[] arrayCellParaClients;
            VisualCollection cellVisualsCollection;
            int sourceIndex; 

            QueryRowDetails( 
                pfstablerow, 
                out arrayFsCell,
                out arrayUpdate, 
                out arrayTableCellMerge);

            //  arrayFsCell lists cells in order different from one we want to maintain in visual collection.
            //  before going to update visual collection it is necessary to reorder ascending by cell's column index. 
            //  knowing the following facts:
            //  * total number of columns the row holds (including row spanned cells from previous rows) less or equal to tableColumnCount; 
            //  * total number of cells (including row spanned cells from previous rows) is less or equal to tableColumnCount; 
            //  * cells do not overlap - no two cells have the same column index;
            //  * cells' column indices fall into the range [0, tableColumnCount - 1]; 
            //  it is possible to write custom and optimized sorting routine:
            //  * iterate through arrayFsCell;
            //  * for each item record its CellParaClient value into arrayCellParaClients[CellParaClient.ColumnIndex];
            //  once complete, arrayCellParaClients will contain CellParaClients in correct order. some entries however will be null 
            //  due to potential column spanning of cells.
            arrayCellParaClients = new CellParaClientEntry[tableColumnCount]; 
 
            for (int iC = 0; iC < arrayFsCell.Length; ++iC)
            { 
                CellParaClient cellParaClient;
                PTS.FSKUPDATE fskupdCell;
                int columnIndex;
 
                if (arrayFsCell[iC] == IntPtr.Zero)
                { 
                    //  paginated case - cell may be null 
                    continue;
                } 

                fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited)
                    ? arrayUpdate[iC]
                    : fskupdRow; 

                cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                columnIndex = cellParaClient.ColumnIndex; 
                arrayCellParaClients[columnIndex].cellParaClient = cellParaClient;
                arrayCellParaClients[columnIndex].fskupdCell = fskupdCell; 
            }

            cellVisualsCollection = rowVisual.Children;
            sourceIndex = 0; 

            for (int columnIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) 
            { 
                CellParaClient cellParaClient;
                double urCellOffset; 
                PTS.FSKUPDATE fskupdCell;

                cellParaClient = arrayCellParaClients[columnIndex].cellParaClient;
                if (cellParaClient == null) 
                {
                    //  paginated case - cell may be null 
                    continue; 
                }
 
                fskupdCell = arrayCellParaClients[columnIndex].fskupdCell;

                if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange)
                { 
                    urCellOffset = calculatedColumns[columnIndex].UrOffset;
                    cellParaClient.ValidateVisual(); 
 
                    if (fskupdCell == PTS.FSKUPDATE.fskupdNew)
                    { 
                        cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual);
                    }
                    else
                    { 
                        Debug.Assert(   cellParaClient.Visual != null
                                    //  If the check below fails, then PTS cheats by reporting "ChangInside" for 
                                    //  a cell that in fact was re-Formatted. 
                                    &&  VisualTreeHelper.GetParent(cellParaClient.Visual) != null   );
 
                        SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual);
                    }
                }
                sourceIndex++; 
            }
 
            if (cellVisualsCollection.Count > sourceIndex) 
            {
                cellVisualsCollection.RemoveRange( 
                    sourceIndex,
                    cellVisualsCollection.Count - sourceIndex);
            }
 
            #if DEBUGDEBUG
            for (int columnIndex = 0, sourceIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) 
            { 
                CellParaClient cellParaClient = arrayCellParaClients[columnIndex].cellParaClient;
                if (cellParaClient != null) 
                {
                    Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex);
                    sourceIndex++;
                } 
            }
            #endif // DEBUGDEBUG 
        } 

        ///  
        /// Draws the backgrounds for all columns in the table
        /// 
        /// Drawing Context.
        /// Content rect of table. 
        private void DrawColumnBackgrounds(DrawingContext dc, Rect tableContentRect)
        { 
            double columnStart = tableContentRect.X; 
            Rect colRect = tableContentRect;
            Brush columnBackgroundBrush; 

            if (ThisFlowDirection != PageFlowDirection)
            {
                for (int iC = CalculatedColumns.Length-1; iC >= 0; iC--) 
                {
                    columnBackgroundBrush = (iC < Table.Columns.Count) 
                        ? Table.Columns[iC].Background 
                        : null;
 
                    colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing;

                    if (columnBackgroundBrush != null)
                    { 
                        colRect.X = columnStart;
                        dc.DrawRectangle(columnBackgroundBrush, null, colRect); 
                    } 

                    columnStart += colRect.Width; 
                }
            }
            else
            { 
                for (int iC = 0; iC < CalculatedColumns.Length; iC++)
                { 
                    columnBackgroundBrush = (iC < Table.Columns.Count) 
                        ? Table.Columns[iC].Background
                        : null; 

                    colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing;

                    if (columnBackgroundBrush != null) 
                    {
                        colRect.X = columnStart; 
                        dc.DrawRectangle(columnBackgroundBrush, null, colRect); 
                    }
 
                    columnStart += colRect.Width;
                }
            }
        } 

        ///  
        /// Calculates actual row height, subtracting what was previously reported as dvrBeforeTopRow and dvrAfterBottomRow 
        /// 
        private double GetActualRowHeight(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, int rowIndex, MbpInfo mbpInfo) 
        {
            int dvAdjustment = 0;

            if(IsFirstChunk && rowIndex == 0) 
            {
                dvAdjustment = -mbpInfo.BPTop; 
            } 

            if(IsLastChunk && rowIndex == arrayTableRowDesc.Length - 1) 
            {
                dvAdjustment = -mbpInfo.BPBottom;
            }
 
            return TextDpi.FromTextDpi(arrayTableRowDesc[rowIndex].u.dvrRow + dvAdjustment);
        } 
 
        /// 
        /// Draws TableRowGroup backgrounds 
        /// 
        /// DrawingContext to draw in.
        /// Row descriptions.
        /// External rect for this chunk of table. 
        /// Margin/Border/Padding info for table.
        ///  
        /// 
        /// 
        ///  
        /// Critical - as this uses unmanaged data structures with a union
        /// 
        [SecurityCritical]
        private void DrawRowGroupBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo) 
        {
            double rowGroupTop = tableContentRect.Y; 
            double rowGroupHeight = 0; 
            Rect rowRect = tableContentRect;
            Brush rowGroupBackgroundBrush; 

            if(arrayTableRowDesc.Length > 0)
            {
                TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow))).Row; 
                TableRowGroup tableRowGroup = row.RowGroup;
 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; 

                    if (tableRowGroup != row.RowGroup)
                    {
                        rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty); 

                        if (rowGroupBackgroundBrush != null) 
                        { 
                            rowRect.Y = rowGroupTop;
                            rowRect.Height = rowGroupHeight; 
                            dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect);
                        }

                        rowGroupTop += rowGroupHeight; 
                        tableRowGroup = row.RowGroup;
                        rowGroupHeight = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); 
                    } 
                    else
                    { 
                        rowGroupHeight += GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo);
                    }
                }
 
                rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty);
                if (rowGroupBackgroundBrush != null) 
                { 
                    rowRect.Y = rowGroupTop;
                    rowRect.Height = rowGroupHeight; 
                    dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect);
                }
            }
 
        }
 
        ///  
        /// Draws TableRow backgrounds
        ///  
        /// DrawingContext to draw in.
        /// Row descriptions.
        /// External rect for this chunk of table.
        /// Margin/Border/Padding info for table. 
        /// 
        /// 
        ///  
        /// 
        /// Critical - as this uses unmanaged data structures with a union 
        /// 
        [SecurityCritical]
        private void DrawRowBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo)
        { 
            double rowTop = tableContentRect.Y;
            Rect rowRect = tableContentRect; 
 
            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
            { 
                TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row;
                Brush rowBackgroundBrush = (Brush)row.GetValue(TextElement.BackgroundProperty);

                rowRect.Y = rowTop; 
                rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo);
 
                if (rowBackgroundBrush != null) 
                {
                    dc.DrawRectangle(rowBackgroundBrush, null, rowRect); 
                }

                rowTop += rowRect.Height; // Adjust for top of next row
            } 
        }
 
        ///  
        /// ValidateCalculatedColumns
        ///  
        /// 
        /// Side effect: _durMinWidth and _durMaxWidth are also updated.
        /// 
        private void ValidateCalculatedColumns() 
        {
            double totalPadding; 
            int columns = Table.ColumnCount; 

            if (_calculatedColumns == null) 
            {
                _calculatedColumns = new CalculatedColumn[columns];
            }
            else if (_calculatedColumns.Length != columns) 
            {
                CalculatedColumn[] newCalculatedColumns = new CalculatedColumn[columns]; 
 
                Array.Copy(
                    _calculatedColumns, 
                    newCalculatedColumns,
                    Math.Min(_calculatedColumns.Length, columns));

                _calculatedColumns = newCalculatedColumns; 
            }
 
            if (_calculatedColumns.Length > 0) 
            {
                int i = 0; 

                while (i < _calculatedColumns.Length && i < Table.Columns.Count)
                {
                    _calculatedColumns[i].UserWidth = Table.Columns[i].Width; 
                    i++;
                } 
 
                while (i < _calculatedColumns.Length)
                { 
                    _calculatedColumns[i].UserWidth = TableColumn.DefaultWidth;
                    i++;
                }
            } 

            _durMinWidth = _durMaxWidth = 0; 
 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            { 
                switch (_calculatedColumns[i].UserWidth.GridUnitType)
                {
                    case (GridUnitType.Auto):
                        // 

                        _calculatedColumns[i].ValidateAuto(1.0, 10e5); 
                        break; 

                    case (GridUnitType.Star): 
                        _calculatedColumns[i].ValidateAuto(1.0, 10e5);
                        break;

                    case (GridUnitType.Pixel): 
                        _calculatedColumns[i].ValidateAuto(
                            _calculatedColumns[i].UserWidth.Value, 
                            _calculatedColumns[i].UserWidth.Value); 
                        break;
 
                    default:
                        Debug.Assert(false, "Unsupported unit type");
                        break;
                } 
                _durMinWidth += _calculatedColumns[i].DurMinWidth;
                _durMaxWidth += _calculatedColumns[i].DurMaxWidth; 
            } 

            // Use durAvailable as width limit for MBP, and MaxWidth as height limit since height values will not be used 
            MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element);

            totalPadding = Table.InternalCellSpacing * Table.ColumnCount + mbpInfo.Margin.Left + mbpInfo.Border.Left + mbpInfo.Padding.Left + mbpInfo.Padding.Right + mbpInfo.Border.Right + mbpInfo.Margin.Right;
            _durMinWidth += totalPadding; 
            _durMaxWidth += totalPadding;
        } 
 
        /// 
        /// ValidateTableWidths 
        /// 
        /// Availble width
        /// Resulting table width
        ///  
        /// PTS.True when no changes in columns width detected,
        /// PTS.False otherwise 
        ///  
        private int ValidateTableWidths(double durAvailableWidth, out double durTableWidth)
        { 
            bool exactWidth = false;                //  assume that width should never be "exact"
            double durAbsoluteMin, durAbsoluteMax;  //  sum of min and max for all user absolute specified columns
            double durScalableMin;                  //  sum if min for all user percent specified columns
            double durAutoMin, durAutoMax;          //  sum of min and max for all non-specified columns 
            double mul, div, fP;
            double iPercent, iP; 
            double durAbsoluteWidths;               //  calculated sum of widths for all absolute columns 
            double durAbsoluteAndAutoWidths;        //  calculated sum of widths for all non-scalable columns
            double durAutoWidths;                   //  calculated sum of widths for all non-specified columns 
            bool fUseMax = false, fUseMin = false, fUseMaxMax = false;
            bool fUseUserMax = false, fUseUserMin = false, fUseUserMaxMax = false;
            bool fSubtract = false, fUserSubtract = false;
            double durTableUserWidth; 
            double cellSpacing = Table.InternalCellSpacing;
            // Use durAvailable for MBP width limits and MaxWidth for height limits. Height values are not used in this calculation. 
            MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element); 

            double durTotalPadding = cellSpacing * Table.ColumnCount + TextDpi.FromTextDpi(mbpInfo.MBPLeft + mbpInfo.MBPRight); 
            int ptsNoWidthChanges = PTS.True;

            // initialize output
            durTableWidth = 0; 

            // 
            // first calc sum of known percent, absolute and auto widths up 
            //
            iPercent = 0; 
            durAbsoluteMin = durAbsoluteMax = 0;
            durScalableMin = 0;
            durAutoMin = durAutoMax = 0;
 
            //
            // also keep track of the maxWidth/percent ratio necessary to 
            // display the table with the columns at max width and the right 
            // percent value
            // 

            // Step 1 - Run through all of our calculated columns, and determine min and max widths (these widths do NOT include cell spacing)
            mul = 0;
            div = 1; 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            { 
                Debug.Assert(_calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Auto || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Star || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Pixel, "Unexpected GridUnitType"); 
                if (_calculatedColumns[i].UserWidth.IsAuto)
                { 
                    Debug.Assert(0 <= _calculatedColumns[i].DurMinWidth && 0 <= _calculatedColumns[i].DurMaxWidth);
                    durAutoMin += _calculatedColumns[i].DurMinWidth;
                    durAutoMax += _calculatedColumns[i].DurMaxWidth;
                } 
                else
                { 
                    if (_calculatedColumns[i].UserWidth.IsStar) 
                    {
                        iP = _calculatedColumns[i].UserWidth.Value; 
                        if (iP < 0)
                        {
                            iP = 0;
                        } 

                        // check for over 100% overflow 
                        if (iPercent + iP > 100) 
                        {
                            iP = 100 - iPercent; 
                            iPercent = 100;
                            _calculatedColumns[i].UserWidth = new GridLength(iP, GridUnitType.Star);
                        }
                        else 
                        {
                            iPercent += iP; 
                        } 

                        if (iP == 0) 
                        {
                            // non empty cell should get at least 1%
                            iP = 1;
                        } 

                        // remember maxWidth/percentage ratio 
                        if (_calculatedColumns[i].DurMaxWidth * div > iP * mul) 
                        {
                            mul = _calculatedColumns[i].DurMaxWidth; 
                            div = iP;
                        }

                        durScalableMin += _calculatedColumns[i].DurMinWidth; 
                    }
                    else 
                    { 
                        durAbsoluteMin += _calculatedColumns[i].DurMinWidth;
                        durAbsoluteMax += _calculatedColumns[i].DurMaxWidth; 
                    }
                }
            }
 
            // iP is what left from the 100%
 
            // Step 2 - Calculate table user width - This width includes all padding and is a percentage of durAvailableWidth or _durMinWidth / _durMaxWidth, 
            // all three values include padding.
 
            iP = 100 - iPercent;
            Debug.Assert(0 < iP || DoubleUtil.IsZero(iP));
            if (exactWidth)
            { 
                // have no choice - table's parent requires the width to be exact
                durTableUserWidth = durAvailableWidth; 
                if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) 
                {
                    durTableUserWidth = _durMinWidth; 
                }
            }
            else if (0 < iPercent)  // if user width is not given back calculate it from the maxWidth/percentage ratio
            { 
                // check if the remaining user and normal columns are requiring bigger ratio
                if (0 < iP) 
                { 
                    double tl = (durAbsoluteMax + durAutoMax) * div;
                    double tr = iP * mul; 

                    // floating point (tl > tr)
                    if (tl > tr && DoubleUtil.AreClose(tl, tr) == false)
                    { 
                        mul = durAbsoluteMax + durAutoMax;
                        div = iP; 
                    } 
                }
 
                //
                // if there is percentage left or there are only percentage columns use the ratio
                // to back-calculate the table width
                // 
                if ((0 < iP) || DoubleUtil.IsZero(durAbsoluteMax + durAutoMax))
                { 
                    durTableUserWidth = mul * 100 / div + durTotalPadding; 

                    // floating point (durTableUserWidth > durAvailableWidth) 
                    if (durTableUserWidth > durAvailableWidth && DoubleUtil.AreClose(durTableUserWidth, durAvailableWidth) == false)
                    {
                        durTableUserWidth = durAvailableWidth;
                    } 
                }
                else 
                { 
                    // otherwise use available width
                    durTableUserWidth = durAvailableWidth; 
                }

                // floating point (durTableUserWidth < _durMinWidth)
                if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) 
                {
                    durTableUserWidth = _durMinWidth; 
                } 
            }
            else 
            {
                // floating point (_durMaxWidth < durAvailableWidth)
                if (_durMaxWidth < durAvailableWidth && DoubleUtil.AreClose(_durMaxWidth, durAvailableWidth) == false)
                { 
                    // use max value if that smaller the parent size
                    durTableUserWidth = _durMaxWidth; 
                } 
                // floating point (_durMinWidth > durAvailableWidth)
                else if (_durMinWidth > durAvailableWidth && DoubleUtil.AreClose(_durMinWidth, durAvailableWidth) == false) 
                {
                    // have to use min if that is bigger the parent
                    durTableUserWidth = _durMinWidth;
                } 
                else
                { 
                    // use parent between min and max 
                    durTableUserWidth = durAvailableWidth;
                } 
            }

            // subtract padding width which contains border and cellspacing
            // floating point (durTableUserWidth >= durTotalPadding) 

            // Step 3 - Remove padding - ( 
            if (durTableUserWidth > durTotalPadding || DoubleUtil.AreClose(durTableUserWidth, durTotalPadding)) 
            {
                durTableUserWidth -= durTotalPadding; 
            }

            // floating point (0 < (durAutoMax + durAbsoluteMax))
            // Step 4 - Calculate the space for 'auto' sized columns. 
            if (0 < (durAutoMax + durAbsoluteMax) && DoubleUtil.IsZero(durAutoMax + durAbsoluteMax) == false)
            { 
                // cache width remaining for normal and user columns over percent columns (durAbsoluteAndAutoWidths) 
                durAbsoluteAndAutoWidths = iP * durTableUserWidth / 100;
 
                // floating point (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin))
                if (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durAbsoluteMin + durAutoMin)) == false)
                {
                    durAbsoluteAndAutoWidths = durAbsoluteMin + durAutoMin; 
                }
 
                // floating point (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin)) 
                if (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durTableUserWidth - durScalableMin)) == false)
                { 
                    durAbsoluteAndAutoWidths = durTableUserWidth - durScalableMin;
                }
            }
            else 
            {
                // all widths is for percent columns 
                durAbsoluteAndAutoWidths = 0; 
            }
 
            //
            // distribute remaining width amongst normal and user columns
            // first try to use max width for user columns and normal columns
            // 
            // floating point (0 < durAbsoluteMax)
            if (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) 
            { 
                durAbsoluteWidths = durAbsoluteMax;
                if (durAbsoluteWidths > durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) 
                {
                    durAbsoluteWidths = durAbsoluteAndAutoWidths;
                }
 
                // floating point (0 < durAutoMax)
                if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                { 
                    durAutoWidths = durAutoMin;
 
                    // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths)
                    if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths))
                    {
                        durAutoWidths = durAbsoluteAndAutoWidths - durAbsoluteWidths; 
                    }
                    else 
                    { 
                        durAbsoluteWidths = durAbsoluteMin;
 
                        // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths)
                        if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths))
                        {
                            durAbsoluteWidths = durAbsoluteAndAutoWidths - durAutoWidths; 
                        }
                    } 
                } 
                else
                { 
                    durAutoWidths = 0;

                    // floating point (durAbsoluteWidths < durAbsoluteAndAutoWidths)
                    if (durAbsoluteWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) 
                    {
                        durAbsoluteWidths = durAbsoluteAndAutoWidths; 
                    } 
                }
            } 
            else
            {
                durAbsoluteWidths = 0;
 
                // floating point (0 < durAutoMax)
                if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                { 
                    durAutoWidths = durAutoMin;
 
                    // floating point (durAutoWidths < durAbsoluteAndAutoWidths)
                    if (durAutoWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAutoWidths, durAbsoluteAndAutoWidths) == false)
                    {
                        durAutoWidths = durAbsoluteAndAutoWidths; 
                    }
                } 
                else 
                {
                    durAutoWidths = 0; 
                }
            }

            // floating point (durAutoWidths > durAutoMax) 
            if (durAutoWidths > durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false)
            { 
                fUseMaxMax = true; 
            }
            // floating point (durAutoWidths == durAutoMax) 
            else if (DoubleUtil.AreClose(durAutoWidths, durAutoMax))
            {
                fUseMax = true;
            } 
            // floating point (durAutoWidths == durAutoMin)
            else if (DoubleUtil.AreClose(durAutoWidths, durAutoMin)) 
            { 
                fUseMin = true;
            } 
            // floating point (durAutoWidths < durAutoMax)
            else if (durAutoWidths < durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false)
            {
                fSubtract = true; 
            }
 
            // floating point (durAbsoluteWidths > durAbsoluteMax) 
            if (durAbsoluteWidths > durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false)
            { 
                fUseUserMaxMax = true;
            }
            // floating point (durAbsoluteWidths == durAbsoluteMax)
            else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax)) 
            {
                fUseUserMax = true; 
            } 
            // floating point (durAbsoluteWidths == durAbsoluteMin)
            else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMin)) 
            {
                fUseUserMin = true;
            }
            // floating point (durAbsoluteWidths < durAbsoluteMax) 
            else if (durAbsoluteWidths < durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false)
            { 
                fUserSubtract = true; 
            }
 
            // calculate real percentage of percent columns in the table now using the final widths
            fP = (0 < durTableUserWidth) ? (100 * (durTableUserWidth - durAbsoluteWidths - durAutoWidths) / durTableUserWidth) : 0;

            // start with content area of table 
            durTableWidth = TextDpi.FromTextDpi(mbpInfo.BPLeft);
            // 
            // now go and calculate column widths by distributing the extra width over 
            // the min width or subtracting the extra width from max
            // 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            {
                if (_calculatedColumns[i].UserWidth.IsAuto)
                { 
                    // adjust normal column by adding to min or subtracting from max
                    _calculatedColumns[i].DurWidth = 
                        fSubtract 
                        ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAutoMax - durAutoWidths) / (durAutoMax - durAutoMin))
                        : fUseMaxMax 
                        ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMax) / durAutoMax)
                        : fUseMax
                        ? _calculatedColumns[i].DurMaxWidth
                        : fUseMin 
                        ? _calculatedColumns[i].DurMinWidth
                          // floating point (0 < durAutoMax) 
                        : (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                        ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMin) / durAutoMax)
                        : 0; 
                }
                else if (_calculatedColumns[i].UserWidth.IsStar)
                {
                    // 
                    // if percent first calculate the width from the percent
                    // 
                    durAbsoluteAndAutoWidths = 
                        (0 < iPercent)
                        ? (durTableUserWidth * (fP * _calculatedColumns[i].UserWidth.Value / iPercent) / 100) 
                        : 0;

                    //
                    // make sure it is over the min width 
                    //
                    durAbsoluteAndAutoWidths -= _calculatedColumns[i].DurMinWidth; 
 
                    // floating point (durAbsoluteAndAutoWidths < 0)
                    if (durAbsoluteAndAutoWidths < 0 && DoubleUtil.IsZero(durAbsoluteAndAutoWidths) == false) 
                    {
                        durAbsoluteAndAutoWidths = 0;
                    }
 
                    _calculatedColumns[i].DurWidth = (_calculatedColumns[i].DurMinWidth + durAbsoluteAndAutoWidths);
                } 
                else 
                {
                    Debug.Assert(_calculatedColumns[i].UserWidth.IsAbsolute); 

                    // adjust user column by adding to min or subtracting from max
                    _calculatedColumns[i].DurWidth =
                        fUserSubtract 
                        // table needs to be (durAbsoluteMax - durAbsoluteWidths) shorter, so subtract width
                        // from columns proportionally to (_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) 
                        ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAbsoluteMax - durAbsoluteWidths) / (durAbsoluteMax - durAbsoluteMin)) 
                        : fUseUserMaxMax
                        ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMax) / durAbsoluteMax) 
                        : fUseUserMax
                        ? _calculatedColumns[i].DurMaxWidth
                        : fUseUserMin
                        ? _calculatedColumns[i].DurMinWidth 
                        // floating point (0 < durAbsoluteMax)
                        : (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) 
                        ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMin) / durAbsoluteMax) 
                        : 0;
                } 

                Debug.Assert(_calculatedColumns[i].DurMinWidth <= _calculatedColumns[i].DurMaxWidth);
                // Cells themselves are 1/2 cell spacing inside column
                _calculatedColumns[i].UrOffset = durTableWidth + cellSpacing / 2.0; 

                durTableWidth += _calculatedColumns[i].DurWidth + cellSpacing; // Advance to next column 
 
                if (_calculatedColumns[i].PtsWidthChanged == PTS.True)
                { 
                    ptsNoWidthChanges = PTS.False;
                }
            }
 
            durTableWidth += mbpInfo.Margin.Left + TextDpi.FromTextDpi(mbpInfo.MBPRight);
 
            return (ptsNoWidthChanges); 
        }
 
        /// 
        /// Content rect - Rect from which rows/rowgroups/columns are calculated.
        /// 
        private PTS.FSRECT GetTableContentRect(MbpInfo mbpInfo) 
        {
            int calculatedBPTop = IsFirstChunk ? mbpInfo.BPTop : 0; 
            int calculatedBPBottom = IsLastChunk ? mbpInfo.BPBottom : 0; 

            return new PTS.FSRECT(_rect.u + mbpInfo.BPLeft, 
                                  _rect.v + calculatedBPTop,
                                  Math.Max(_rect.du - (mbpInfo.BPRight + mbpInfo.BPLeft), 1),
                                  Math.Max(_rect.dv - calculatedBPBottom - calculatedBPTop, 1)
                                 ); 
        }
 
        ///  
        /// Returns the offset from the table top to the first row of the table for the current chunk.
        ///  
        private int GetTableOffsetFirstRowTop()
        {
            MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element);
 
            return IsFirstChunk ? mbp.BPTop : 0;
        } 
 
        #endregion Private Methods
 
        //-----------------------------------------------------
        //
        //  Private Classes / Structures
        // 
        //------------------------------------------------------
 
        #region Private Classes / Structures 

        private struct CellParaClientEntry 
        {
            internal CellParaClient cellParaClient;
            internal PTS.FSKUPDATE fskupdCell;
        } 

        #endregion Private Classes / Structures 
 
        //-----------------------------------------------------
        // 
        //  Private Data
        //
        //------------------------------------------------------
 
        #region Private Data
 
        // Rectangle of column table para client is in. 
        private PTS.FSRECT _columnRect;
        private CalculatedColumn[] _calculatedColumns;  //  layout related information per column 

        private double _durMinWidth;                    //  minimum width of the table
        private double _durMaxWidth;                    //  maximum wudth of the table
 
        private double _previousAutofitWidth = 0.0;     // Autofit width on previous layout pass.
        private double _previousTableWidth = 0.0;       // Table width on previous layout pass. 
 
        #endregion Private Data
    } 
}


// 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.
//  
//
// 
// Description: TableParaClient holds display related information. 
//
// History: 
//  05/19/2003 : olego - Created
//
//---------------------------------------------------------------------------
 
using MS.Internal;
using MS.Internal.PtsTable; 
using MS.Internal.Text; 
using MS.Internal.Documents;
using System; 
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Security;
using System.Windows; 
using System.Windows.Documents; 
using System.Windows.Media;
 
using MS.Internal.PtsHost.UnsafeNativeMethods;

namespace MS.Internal.PtsHost
{ 
    /// 
    /// Table display data 
    ///  
    internal sealed class TableParaClient : BaseParaClient
    { 
        //-----------------------------------------------------
        //
        //  Constructors
        // 
        //-----------------------------------------------------
 
        #region Constructors 

        ///  
        /// Constructor
        /// 
        /// Owner of the para client
        internal TableParaClient(TableParagraph paragraph) : base(paragraph) 
        {
        } 
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Protected Methods
        // 
        //-----------------------------------------------------
 
        #region Protected Methods 

        ///  
        /// Arrange table paragraph by calling Arrange on all cells
        /// 
        /// 
        /// Critical, because: 
        ///     a) calls Critical function QueryRowDetails.
        ///     b) calls Critical function PTS.FsTransformRectangle 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        protected override void OnArrange()
        {
            Debug.Assert(   Table != null 
                        &&  CalculatedColumns != null  );
 
            base.OnArrange(); 

            _columnRect = Paragraph.StructuralCache.CurrentArrangeContext.ColumnRect; 

            CalculatedColumn[] calculatedColumns = CalculatedColumns;
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rect;
 
            if (    //  table has no rows thus no cells to arrange 
                    !QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect)
                    //  no changes requiring arrange in the table 
                ||  fskupdTable == PTS.FSKUPDATE.fskupdNoChange
                ||  fskupdTable == PTS.FSKUPDATE.fskupdShifted  )
            {
                return; 
            }
 
            // Rect may have moved from collision with figure or floater. 
            _rect = rect;
 
            // Update chunk information
            UpdateChunkInfo(arrayTableRowDesc);

            MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element); 

            if(ParentFlowDirection != PageFlowDirection) 
            { 
                PTS.FSRECT pageRect = _pageContext.PageRect;
 
                PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ParentFlowDirection), ref pageRect, ref _rect, PTS.FlowDirectionToFswdir(PageFlowDirection), out _rect));
                mbp.MirrorMargin();
            }
 
            _rect.u += mbp.MarginLeft;
            _rect.du -= mbp.MarginLeft + mbp.MarginRight; 
 
            int vrRowTop = GetTableOffsetFirstRowTop() + TextDpi.ToTextDpi(Table.InternalCellSpacing) / 2;
 
            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
            {
                PTS.FSKUPDATE fskupdRow;
                PTS.FSKUPDATE[] arrayUpdate; 
                IntPtr[] arrayFsCell;
                PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
 
                fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited)
                    ? arrayTableRowDesc[iR].fsupdinf.fskupd 
                    : fskupdTable;

                if (fskupdRow == PTS.FSKUPDATE.fskupdNoChange)
                { 
                    //  no changes requiring arrange in the row
                    vrRowTop += arrayTableRowDesc[iR].u.dvrRow; 
                    continue; 
                }
 
                QueryRowDetails(
                    arrayTableRowDesc[iR].pfstablerow,
                    out arrayFsCell,
                    out arrayUpdate, 
                    out arrayTableCellMerge);
 
                for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                {
                    if (arrayFsCell[iC] == IntPtr.Zero) 
                    {
                        //  paginated case - cell may be null
                        continue;
                    } 

                    if (    iR != 0 //  paginated case - row spanned cells appearing in first row on the page must be accounted 
                        &&  (   arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle 
                            ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast  )   )
                    { 
                        // this cell has been accounted
                        continue;
                    }
 
                    CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
 
                    double urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset; 

                    cellParaClient.Arrange(TextDpi.ToTextDpi(urCellOffset), vrRowTop, _rect, ThisFlowDirection, _pageContext); 

                }

                vrRowTop += arrayTableRowDesc[iR].u.dvrRow; 

                if (iR == 0 && IsFirstChunk) 
                { 
                    // Remove extra spacing we reported to PTS in RowProperties for top row in table
                    vrRowTop -= mbp.BPTop; 
                }
            }
        }
 

        #endregion Protected Methods 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        ///  
        /// Validate table visual
        ///  
        /// PTS update info
        /// 
        /// Critical - as this calls Critical function ValidateRowVisualComplex and
        ///            ValidateRowVisualSimple. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) 
        {
            Invariant.Assert( fskupdInherited != PTS.FSKUPDATE.fskupdInherited );
            Invariant.Assert( TableParagraph.Table != null && CalculatedColumns != null );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rect; 
            Table table = TableParagraph.Table;
 
            Visual.Clip = new RectangleGeometry(_columnRect.FromTextDpi());

            if (!QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect))
            { 
                //  table has no rows thus no cell to validate
                _visual.Children.Clear(); 
                return; 
            }
 
            MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element);
            if (ThisFlowDirection != PageFlowDirection)
            {
                mbpInfo.MirrorBP(); 
            }
 
            if (fskupdTable == PTS.FSKUPDATE.fskupdInherited) 
            {
                fskupdTable = fskupdInherited; 
            }

            if (fskupdTable == PTS.FSKUPDATE.fskupdNoChange)
            { 
                //  no need to arrange because nothing changed
                return; 
            } 

            if (fskupdTable == PTS.FSKUPDATE.fskupdShifted) 
            {
                fskupdTable = PTS.FSKUPDATE.fskupdNew;
            }
 
            VisualCollection rowVisualsCollection = _visual.Children;
            if (fskupdTable == PTS.FSKUPDATE.fskupdNew) 
            { 
                rowVisualsCollection.Clear();
            } 

            // Draw border and background info.
            Brush backgroundBrush = (Brush)Paragraph.Element.GetValue(TextElement.BackgroundProperty);
 
            using (DrawingContext dc = _visual.RenderOpen())
            { 
                Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi(); 

                _visual.DrawBackgroundAndBorderIntoContext(dc, backgroundBrush, mbpInfo.BorderBrush, mbpInfo.Border, _rect.FromTextDpi(), IsFirstChunk, IsLastChunk); 

                DrawColumnBackgrounds(dc, tableContentRect);
                DrawRowGroupBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo);
                DrawRowBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo); 
            }
 
            TableRow rowPrevious = null; 

            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
            {
                PTS.FSKUPDATE fskupdRow;
                RowParagraph rowParagraph;
                TableRow row; 

                fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited) 
                    ? arrayTableRowDesc[iR].fsupdinf.fskupd 
                    : fskupdTable;
 

                rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow));
                row = rowParagraph.Row;
 
                //
                //  STEP 1 SYNCHRONIZATION 
                //  --------------------------------------------------------- 
                //  synchronize rowVisualCollection.
                //  for newly created rows visual is inserted; 
                //  otherwise for removed rows (if any) corresponding visuals are removed
                //
                if (fskupdRow == PTS.FSKUPDATE.fskupdNew)
                { 
                    RowVisual rowVisual = new RowVisual(row);
                    rowVisualsCollection.Insert(iR, rowVisual); 
                } 
                else
                { 
                    SynchronizeRowVisualsCollection(rowVisualsCollection, iR, row);
                }

                Invariant.Assert(((RowVisual)rowVisualsCollection[iR]).Row == row); 

                // 
                //  STEP 2 CELL VISUALS VALIDATION 
                //  ----------------------------------------------------------
                //  for new or changed rows go inside and validate cells 
                //
                if (    fskupdRow == PTS.FSKUPDATE.fskupdNew
                    ||  fskupdRow == PTS.FSKUPDATE.fskupdChangeInside   )
                { 
                    // paginated case - if first row of a given rowgroup for this para client has foreign cells, they need to
                    // be rendered regardless of merge state 
                    if(rowParagraph.Row.HasForeignCells && (rowPrevious == null || rowPrevious.RowGroup != row.RowGroup)) 
                    {
                        ValidateRowVisualComplex( 
                            (RowVisual)(rowVisualsCollection[iR]),
                            arrayTableRowDesc[iR].pfstablerow,
                            CalculatedColumns.Length,
                            fskupdRow, 
                            CalculatedColumns);
                    } 
                    else 
                    {
                        ValidateRowVisualSimple( 
                            (RowVisual)(rowVisualsCollection[iR]),
                            arrayTableRowDesc[iR].pfstablerow,
                            fskupdRow,
                            CalculatedColumns); 
                    }
                } 
 
                rowPrevious = row;
            } 

            //
            //  STEP 4
 

            if (rowVisualsCollection.Count > arrayTableRowDesc.Length) 
            { 
                rowVisualsCollection.RemoveRange(
                    arrayTableRowDesc.Length, 
                    rowVisualsCollection.Count - arrayTableRowDesc.Length);
            }
        }
 
        /// ------------------
        /// Updates viewport 
        /// -------------------- 
        /// 
        /// Critical - as this calls Critical functions QueryRowDetails. 
        /// Safe - The arrayTableRowDesc parameter passed to other methods is generated securely in this function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override void UpdateViewport(ref PTS.FSRECT viewport) 
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  ); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                { 
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;

                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow, 
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge); 

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        {
                            //  paginated case - cell may be null 
                            continue;
                        } 
 
                        CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        cellParaClient.UpdateViewport(ref viewport); 
                    }
                }
            }
        } 

        ///  
        /// Performs hit testing. 
        /// 
        /// Point of interest. 
        /// Element if any.
        /// 
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal override IInputElement InputHitTest(PTS.FSPOINT pt)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  );

            IInputElement element = null; 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                // Start vrRowTop from 'true' v of table
                int vrRowTop = GetTableOffsetFirstRowTop() + rectTable.v;
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    if (pt.v >= vrRowTop && pt.v <= (vrRowTop + arrayTableRowDesc[iR].u.dvrRow)) 
                    { 
                        PTS.FSKUPDATE[] arrayUpdate;
                        IntPtr[] arrayFsCell; 
                        PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;

                        QueryRowDetails(
                            arrayTableRowDesc[iR].pfstablerow, 
                            out arrayFsCell,
                            out arrayUpdate, 
                            out arrayTableCellMerge); 

                        for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                        {
                            if (arrayFsCell[iC] == IntPtr.Zero)
                            {
                                //  paginated case - cell may be null 
                                continue;
                            } 
 
                            CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                            PTS.FSRECT rect = cellParaClient.Rect; 

                            if (cellParaClient.Rect.Contains(pt))
                            {
                                element = cellParaClient.InputHitTest(pt); 
 								break;
                            } 
                        } 
                        break;
                    } 
                    vrRowTop += arrayTableRowDesc[iR].u.dvrRow;
                }
            }
 
            if(element == null && _rect.Contains(pt))
            { 
                element = TableParagraph.Table; 
            }
 
            return (element);
        }

        ///  
        /// Returns ArrayList of rectangles for element if found.
        ///  
        ///  
        /// ContentElement for which rectangles are to be found.
        ///  
        /// 
        /// int representing start offset of e
        /// 
        ///  
        /// int representing number of positions occupied by element.
        ///  
        ///  
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override List GetRectangles(ContentElement e, int start, int length) 
        {
            Debug.Assert(TableParagraph.Table != null 
                        && CalculatedColumns != null); 
            List rectangles = new List();
 
            if (TableParagraph.Table == e)
            {
                // We have found the element. Return rectangles for this paragraph.
                GetRectanglesForParagraphElement(out rectangles); 
            }
            else 
            { 
                // Search subpage for table cells
                PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
                PTS.FSKUPDATE fskupdTable;
                PTS.FSRECT rectTable;

                rectangles = new List(); 
                if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
                { 
                    for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                    {
                        PTS.FSKUPDATE[] arrayUpdate; 
                        IntPtr[] arrayFsCell;
                        PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
                        QueryRowDetails(
                                arrayTableRowDesc[iR].pfstablerow, 
                                out arrayFsCell,
                                out arrayUpdate, 
                                out arrayTableCellMerge); 
                        for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                        { 
                            if (arrayFsCell[iC] == IntPtr.Zero)
                            {
                                //  paginated case - cell may be null
                                continue; 
                            }
 
                            CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                            if (start < cellParaClient.Paragraph.ParagraphEndCharacterPosition)
                            { 
                                // Element start is within cell boundary
                                rectangles = cellParaClient.GetRectangles(e, start, length);
                                Invariant.Assert(rectangles != null);
                                if (rectangles.Count != 0) 
                                {
                                    break; 
                                } 
                            }
                        } 
                        if (rectangles.Count != 0)
                        {
                            break;
                        } 
                    }
                } 
            } 

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

        ///  
        /// Paragraph results - verification
        ///  
        internal override ParagraphResult CreateParagraphResult() 
        {
            return new TableParagraphResult(this); 
        }

        /// 
        /// Return TextContentRange for the content of the paragraph. 
        /// 
        ///  
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal override TextContentRange GetTextContentRange()
        { 
            TextContentRange range = null;
 
            // Search subpage for table cells 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable;
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                range = new TextContentRange(); 
                UpdateChunkInfo(arrayTableRowDesc);
                TextElement elementOwner = this.Paragraph.Element as TextElement; 
 
                // Add range for Table leading edge, if necessary
                if (_isFirstChunk) 
                    range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                        elementOwner, ElementEdge.BeforeStart));

                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    PTS.FSTABLEROWDETAILS tableRowDetails; 
                    TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; 
                    PTS.Validate(PTS.FsQueryTableObjRowDetails(
                        PtsContext.Context, 
                        arrayTableRowDesc[iR].pfstablerow,
                        out tableRowDetails));

                    if (tableRowDetails.fskboundaryAbove != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) 
                    {
                        // Add range for TableRowGroup leading edge, if necessary 
                        if (row.Index == 0) 
                            range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                                row.RowGroup, ElementEdge.BeforeStart)); 

                        range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                            row, ElementEdge.BeforeStart));
                    } 

                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
                    QueryRowDetails( 
                            arrayTableRowDesc[iR].pfstablerow,
                            out arrayFsCell,
                            out arrayUpdate,
                            out arrayTableCellMerge); 

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    { 
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        { 
                            //  paginated case - cell may be null
                            continue;
                        }
 
                        CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        range.Merge(cellParaClient.GetTextContentRange()); 
                    } 

                    if (tableRowDetails.fskboundaryBelow != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) 
                    {
                        range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                            row, ElementEdge.AfterEnd));
 
                        // Add range for TableRowGroup trailing edge, if necessary
                        if (row.Index == row.RowGroup.Rows.Count - 1) 
                            range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( 
                                row.RowGroup, ElementEdge.AfterEnd));
                    } 
                }

                // Add range for Table trailing edge, if necessary
                if (_isLastChunk) 
                    range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge(
                        elementOwner, ElementEdge.AfterEnd)); 
            } 

            if (range == null) 
            {
                // if table has no rows or no cells
                range = TextContainerHelper.GetTextContentRangeForTextElement(TableParagraph.Table);
            } 

            return range; 
        } 

        ///  
        /// Returns the paragraphs for the appropriate cell, given a point
        /// 
        /// Point to check for cell.
        /// Whether to snap to text 
        /// 
        /// CellParaClient found 
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellParaClientFromPoint(Point point, bool snapToText)
        { 
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
            int u = TextDpi.ToTextDpi(point.X); 
            int v = TextDpi.ToTextDpi(point.Y);
 
            CellParaClient cpcFound = null; 
            CellParaClient cpcClosest = null;
            int iClosestDistance = int.MaxValue; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length && cpcFound == null; ++iR) 
                {
                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge);
 
                    for (int iC = 0; iC < arrayFsCell.Length && cpcFound == null; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                        PTS.FSRECT rect = cpc.Rect; 

                        if (u >= rect.u && u <= (rect.u + rect.du) && 
                            v >= rect.v && v <= (rect.v + rect.dv))
                        {
                            cpcFound = cpc;
                        } 
                        else if(snapToText)
                        { 
                            int du = Math.Min(Math.Abs(rect.u - u), Math.Abs(rect.u + rect.du - u)); 
                            int dv = Math.Min(Math.Abs(rect.v - v), Math.Abs(rect.v + rect.dv - v));
 
                            if( (du + dv) < iClosestDistance)
                            {
                                iClosestDistance = du + dv;
                                cpcClosest = cpc; 
                            }
                        } 
                    } 
                }
            } 

            if(snapToText && cpcFound == null)
            {
                cpcFound = cpcClosest; 
            }
 
            return cpcFound; 
        }
 


        /// 
        /// Returns the paragraphs for all rows in a table 
        /// 
        ///  
        /// True if any child paragraph result has text content 
        /// 
        ///  
        /// Array of paragraph results
        /// 
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetChildrenParagraphResults(out bool hasTextContent) 
        {
            MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element);
            if (ThisFlowDirection != PageFlowDirection)
            { 
                mbpInfo.MirrorBP();
            } 
 
            Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi();
            double rowTop = tableContentRect.Y; 
            Rect rowRect = tableContentRect;
            hasTextContent = false;

            List rowParagraphResults = new List(0); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable; 
            PTS.FSRECT rectTable;
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                {
                    RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow)); 

                    rowRect.Y = rowTop; 
                    rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); 
                    RowParagraphResult rowParagraphResult = new RowParagraphResult(this, iR, rowRect, rowParagraph);
                    if (rowParagraphResult.HasTextContent) 
                    {
                        hasTextContent = true;
                    }
                    rowParagraphResults.Add(rowParagraphResult); 

                    rowTop += rowRect.Height; // Adjust for top of next row 
                } 
            }
 
            return new ReadOnlyCollection(rowParagraphResults);
        }

        ///  
        /// Returns the paragraphs for a given row
        ///  
        /// Row index 
        /// 
        /// Array of paragraph results 
        /// 
        /// 
        /// True if any child paragraph has text content
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetChildrenParagraphResultsForRow(int rowIndex, out bool hasTextContent)
        {
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
 
            List cellParagraphResults = new List(0);
            hasTextContent = false; 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            {
                PTS.FSKUPDATE[] arrayUpdate; 
                IntPtr[] arrayFsCell;
                PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
 
                QueryRowDetails(
                    arrayTableRowDesc[rowIndex].pfstablerow, 
                    out arrayFsCell,
                    out arrayUpdate,
                    out arrayTableCellMerge);
 
                for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                { 
                    if(arrayFsCell[iC] != IntPtr.Zero && 
                        (rowIndex == 0 ||
                          (arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle && 
                           arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast)
                        )
                      )
                    { 
                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
                        ParagraphResult cellParagraphResult = cpc.CreateParagraphResult(); 
                        if (cellParagraphResult.HasTextContent) 
                        {
                            hasTextContent = true; 
                        }
                        cellParagraphResults.Add(cellParagraphResult);
                    }
                } 
            }
 
            return new ReadOnlyCollection(cellParagraphResults); 
        }
 
        /// 
        /// Returns the paragraphs for the appropriate cell, given a point
        /// 
        /// Point to check for cell. 
        /// Whether to snap to text
        ///  
        /// Array of paragraph results 
        /// 
        internal ReadOnlyCollection GetParagraphsFromPoint(Point point, bool snapToText) 
        {
            CellParaClient cpc = GetCellParaClientFromPoint(point, snapToText);

            List listResults = new List(0); 

            if(cpc != null) 
            { 
                listResults.Add(cpc.CreateParagraphResult());
            } 

            return new ReadOnlyCollection(listResults);
        }
 
        /// 
        /// Returns the paragraphs for the appropriate cell, given a point 
        ///  
        /// Position of cell.
        ///  
        /// Array of paragraph results
        /// 
        internal ReadOnlyCollection GetParagraphsFromPosition(ITextPointer position)
        { 
            CellParaClient cpc = GetCellParaClientFromPosition(position);
 
            List listResults = new List(0); 

            if(cpc != null) 
            {
                listResults.Add(cpc.CreateParagraphResult());
            }
 
            return new ReadOnlyCollection(listResults);
        } 
 
        /// 
        /// Returns tight bounding path geometry. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  ); 
 
            Geometry geometry = null;
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
 
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                bool passedEndPosition = false; 

                for (int iR = 0; iR < arrayTableRowDesc.Length && !passedEndPosition; ++iR) 
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell;
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 

                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow, 
                        out arrayFsCell,
                        out arrayUpdate, 
                        out arrayTableCellMerge);

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC)
                    { 
                        if (arrayFsCell[iC] == IntPtr.Zero)
                        { 
                            //  paginated case - cell may be null 
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));

                        if (endPosition.CompareTo(cpc.Cell.ContentStart) <= 0) 
                        {
                            //  remember that at least one cell in this row starts after the range's end. 
                            //  in this case it is safe to break after this whole row is processed. 
                            //  Note: can not break right away because cells in arrayFsCell come not
                            //  necessarily in backing store (cp) order. 
                            passedEndPosition = true;
                        }
                        else
                        { 
                            if (startPosition.CompareTo(cpc.Cell.ContentEnd) <= 0)
                            { 
                                Geometry cellGeometry = cpc.GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, visibleRect); 
                                CaretElement.AddGeometry(ref geometry, cellGeometry);
                            } 
                        }
                    }
                }
            } 

            if (geometry != null) 
            { 
                geometry = Geometry.Combine(geometry, Visual.Clip, GeometryCombineMode.Intersect, null);
            } 
            return geometry;
        }

        ///  
        /// Returns the para client for a cell, given a position
        ///  
        /// Position to check for cell. 
        /// 
        /// Cell para client 
        /// 
        /// 
        /// Critical - as this calls Critical function QueryRowDetails.
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellParaClientFromPosition(ITextPointer position)
        { 
            Debug.Assert(   TableParagraph.Table != null
                        &&  CalculatedColumns != null  );

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
 
            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge);

                    for (int iC = 0; iC < arrayFsCell.Length; ++iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        { 
                            //  paginated case - cell may be null
                            continue; 
                        }

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));
 
                        if(position.CompareTo(cpc.Cell.ContentStart) >= 0 && position.CompareTo(cpc.Cell.ContentEnd) <= 0)
                        { 
                            return cpc; 
                        }
                    } 
                }
            }

            return (null); 
        }
 
 
        /// 
        /// Returns an appropriate found cell 
        /// 
        /// Suggested X position for cell to find.
        /// RowGroupIndex to be above.
        /// RowIndex to be above. 
        /// 
        /// Cell Para Client of cell 
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical
        ///        function.  All In parameters passed in are generated by safe methods.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal CellParaClient GetCellAbove(double suggestedX, int rowGroupIndex, int rowIndex)
        { 
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc;
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;
            int suggestedU = TextDpi.ToTextDpi(suggestedX); 

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            { 
                MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element);
 
                for (int iR = arrayTableRowDesc.Length - 1; iR >= 0; --iR)
                {
                    PTS.FSKUPDATE[] arrayUpdate;
                    IntPtr[] arrayFsCell; 
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
 
                    QueryRowDetails( 
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge);

                    CellParaClient cpcClosest = null; 
                    int iClosestDistance = int.MaxValue;
 
                    for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC) 
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null
                            continue;
                        } 

                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
 
                        // Check if bottom row of cell is less than row index for moving up.
 
                        int cellBottomIndex = cpc.Cell.RowIndex + cpc.Cell.RowSpan - 1;
                        if( (cellBottomIndex < rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) ||
                            (cpc.Cell.RowGroupIndex < rowGroupIndex)
                          ) 
                        {
                            if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) 
                            { 
                                return cpc;
                            } 
                            // Else record it, and return it if we find nothing better for this row.

                            int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU);
 
                            if(iDistance < iClosestDistance)
                            { 
                                iClosestDistance = iDistance; 
                                cpcClosest = cpc;
                            } 
                        }
                    }

                    if(cpcClosest != null) 
                    {
                        return cpcClosest; 
                    } 
                }
            } 

            return (null);
        }
 
        /// 
        /// Returns an appropriate found cell 
        ///  
        /// Suggested X position for cell to find.
        /// RowGroupIndex to be above. 
        /// RowIndex to be below.
        /// 
        /// Cell Para Client of cell
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails. 
        /// Safe - as this can't be used to pass arbitrary parameters to the Critical 
        ///        function.  All In parameters passed in are generated by safe methods.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal CellParaClient GetCellBelow(double suggestedX, int rowGroupIndex, int rowIndex)
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  );
 
            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable; 
            int suggestedU = TextDpi.ToTextDpi(suggestedX);

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable))
            { 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                { 
                    PTS.FSKUPDATE[] arrayUpdate; 
                    IntPtr[] arrayFsCell;
                    PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 

                    QueryRowDetails(
                        arrayTableRowDesc[iR].pfstablerow,
                        out arrayFsCell, 
                        out arrayUpdate,
                        out arrayTableCellMerge); 
 
                    CellParaClient cpcClosest = null;
                    int iClosestDistance = int.MaxValue; 

                    for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC)
                    {
                        if (arrayFsCell[iC] == IntPtr.Zero) 
                        {
                            //  paginated case - cell may be null 
                            continue; 
                        }
 
                        CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC]));

                        if( (cpc.Cell.RowIndex > rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) ||
                            (cpc.Cell.RowGroupIndex > rowGroupIndex) 
                            )
                        { 
                            if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) 
                            {
                                return cpc; 
                            }

                            // Else record it and report it if we find nothing better in this row.
                            int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU); 

                            if(iDistance < iClosestDistance) 
                            { 
                                iClosestDistance = iDistance;
                                cpcClosest = cpc; 
                            }
                        }

                    } 

                    if(cpcClosest != null) 
                    { 
                        return cpcClosest;
                    } 
                }
            }

            return (null); 
        }
 
 
        /// 
        /// Returns a cellinfo structure for a given point 
        /// 
        /// Point to check for cell.
        /// 
        /// CellInfo class 
        /// 
        internal CellInfo GetCellInfoFromPoint(Point point) 
        { 
            CellParaClient cpc = GetCellParaClientFromPoint(point, true);
 
            if(cpc != null)
            {
                return new CellInfo(this, cpc);
            } 

            return null; 
        } 

        ///  
        /// Returns a rect corresponding to a row end position.
        /// 
        /// Position to check against.
        ///  
        /// Rect, area
        ///  
        ///  
        /// Critical - as this uses PTS.FSTABLEROWDESCRIPTION, a structure with
        ///         overlapping fields (to match a union in PTS) 
        /// Safe - returned information is safe to disclose.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Rect GetRectangleFromRowEndPosition(ITextPointer position) 
        {
            Debug.Assert(   TableParagraph.Table != null 
                        &&  CalculatedColumns != null  ); 

            PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; 
            PTS.FSKUPDATE fskupdTable;
            PTS.FSRECT rectTable;

            if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) 
            {
                int vrCur = GetTableOffsetFirstRowTop() + rectTable.v; 
 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
                { 
                    TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row;

                    if(((TextPointer)position).CompareTo(row.ContentEnd) == 0)
                    { 
                        return new Rect( TextDpi.FromTextDpi(rectTable.u + rectTable.du),
                                         TextDpi.FromTextDpi(vrCur), 
                                         1.0, 
                                         TextDpi.FromTextDpi(arrayTableRowDesc[iR].u.dvrRow));
                    } 

                    vrCur += arrayTableRowDesc[iR].u.dvrRow;
                }
            } 

            // Valid row end position should have been specified. 
            Debug.Assert(false); 

            return System.Windows.Rect.Empty; 
        }


 
        /// 
        /// AutofitTable 
        ///  
        /// Direction of Track
        /// Available space 
        /// Table width after autofit. It is the same for all rows
        /// 
        /// durAvailableSpace is invalid due to the unfinished table/footer work in
        /// TableSection.GetPageDimensions (see notes in that method for more info). 
        /// 
        internal void AutofitTable( 
            uint fswdirTrack,                       // IN:  direction of Track 
            int durAvailableSpace,                  // IN:  available space (?)
            out int durTableWidth)                  // OUT: Table width after autofit. It is the same for all rows :) 
        {
            Debug.Assert(Table != null);

            double availableSpace = TextDpi.FromTextDpi(durAvailableSpace); 
            double tableWidth;
 
            Autofit(availableSpace, out tableWidth); 
            durTableWidth = TextDpi.ToTextDpi(tableWidth);
        } 

        /// 
        /// UpdAutofitTable
        ///  
        /// Direction of Track
        /// Available space 
        /// Table width after autofit 
        /// Column width changes
        ///  
        /// durAvailableSpace is invalid due to the unfinished table/footer work in
        /// TableSection.GetPageDimensions (see notes in that method for more info).
        /// 
        internal void UpdAutofitTable( 
            uint fswdirTrack,                       // IN:  direction of Track
            int durAvailableSpace,                  // IN:  available space (?) 
            out int durTableWidth,                  // OUT: Table width after autofit. 
            out int fNoChangeInCellWidths)          // OUT:
        { 
            Debug.Assert(Table != null);

            double availableSpace = TextDpi.FromTextDpi(durAvailableSpace);
            double tableWidth; 
            fNoChangeInCellWidths = Autofit(availableSpace, out tableWidth);
            durTableWidth = TextDpi.ToTextDpi(tableWidth); 
        } 

 
        /// 
        /// Performs autofit calculations
        /// 
        /// Available width for table 
        /// Resulting table width
        ///  
        /// PTS.True when no changes in columns width detected, 
        /// PTS.False otherwise
        ///  
        internal int Autofit(double availableWidth, out double tableWidth)
        {
            int ret = PTS.True;
 
            ValidateCalculatedColumns();
 
            if (!DoubleUtil.AreClose(availableWidth, _previousAutofitWidth)) 
            {
                ret = ValidateTableWidths(availableWidth, out tableWidth); 
            }
            else
            {
                tableWidth = _previousTableWidth; 
            }
 
            _previousAutofitWidth = availableWidth; 
            _previousTableWidth = tableWidth;
 
            return ret;
        }

 
        #endregion Internal Methods
 
        //----------------------------------------------------- 
        //
        //  Internal Properties 
        //
        //-----------------------------------------------------

        #region Internal Properties 

        ///  
        /// Table paragraph created the client 
        /// 
        internal TableParagraph TableParagraph 
        {
            get { return (TableParagraph)_paragraph; }
        }
 
        /// 
        /// Table object proper 
        ///  
        internal Table Table
        { 
            get { return TableParagraph.Table; }
        }

 
        /// 
        /// CurrentPage Desired Width 
        ///  
        internal double TableDesiredWidth
        { 
            get
            {
                // Use current format context's page width to limit cell spacing
                double durRet = 0; 

                CalculatedColumn[] cols = CalculatedColumns; 
 
                for (int i = 0; i < cols.Length; i++)
                { 
                    durRet += cols[i].DurWidth + Table.InternalCellSpacing;
                }

                return durRet; 
            }
        } 
 
        /// 
        /// CalculatedColumns 
        /// 
        internal CalculatedColumn[] CalculatedColumns
        {
            get 
            {
                return (_calculatedColumns); 
            } 
        }
 
        /// 
        /// Autofit width - what width we used when autofitting the table.
        /// 
        internal double AutofitWidth 
        {
            get { return _previousAutofitWidth; } 
        } 

        // ------------------------------------------------------------------ 
        // Is this the first chunk of paginated content.
        // -----------------------------------------------------------------
        internal override bool IsFirstChunk { get { return _isFirstChunk; } }
        private bool _isFirstChunk; 

        // ------------------------------------------------------------------ 
        // Is this the last chunk of paginated content. 
        // ------------------------------------------------------------------
        internal override bool IsLastChunk { get { return _isLastChunk; } } 
        private bool _isLastChunk;

        #endregion Internal Properties
 
        //-----------------------------------------------------
        // 
        //  Private Methods 
        //
        //------------------------------------------------------ 

        #region Private Methods

        // ----------------------------------------------------------------- 
        // Update information about first/last chunk.
        // ----------------------------------------------------------------- 
        ///  
        /// Queries PTS and saves information about this paginated chunk
        ///  
        /// Table Row descriptions
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTableObjRowDetails
        ///  
        [SecurityCritical]
        private unsafe void UpdateChunkInfo(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc) 
        { 
            PTS.FSTABLEROWDETAILS tableRowDetails;
 
            RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow));
            TableRow row = rowParagraph.Row;

            PTS.Validate(PTS.FsQueryTableObjRowDetails( 
                PtsContext.Context,
                arrayTableRowDesc[0].pfstablerow, 
                out tableRowDetails)); 

            _isFirstChunk = (tableRowDetails.fskboundaryAbove == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) && 
                (row.Index == 0) && Table.IsFirstNonEmptyRowGroup(row.RowGroup.Index);

            row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[arrayTableRowDesc.Length - 1].fsnmRow))).Row;
 
            PTS.Validate(PTS.FsQueryTableObjRowDetails(
                PtsContext.Context, 
                arrayTableRowDesc[arrayTableRowDesc.Length - 1].pfstablerow, 
                out tableRowDetails));
 
            _isLastChunk = (tableRowDetails.fskboundaryBelow == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) &&
                (row.Index == row.RowGroup.Rows.Count - 1) && Table.IsLastNonEmptyRowGroup(row.RowGroup.Index);
        }
 

        ///  
        /// Queries PTS and returns table detailed structure / layout information about rows. 
        /// 
        /// Storage for table rows information 
        /// Result of update
        /// Rect of the table
        /// true when table has content; false otherwise
        ///  
        /// Critical - as this calls Critical functions PTS.FsQueryTableObjDetails,
        ///            PTS.FsQueryTableObjTableProperDetails and PTS.FsQueryTableObjRowList. 
        /// Safe - returned information is safe to disclose. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private unsafe bool QueryTableDetails(
            out PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc,
            out PTS.FSKUPDATE fskupdTable,
            out PTS.FSRECT rect) 
        {
            PTS.FSTABLEOBJDETAILS tableObjDetails; 
            PTS.FSTABLEDETAILS tableDetails; 

            PTS.Validate(PTS.FsQueryTableObjDetails( 
                PtsContext.Context,
                _paraHandle.Value,
                out tableObjDetails));
            Debug.Assert(TableParagraph == (TableParagraph)(PtsContext.HandleToObject(tableObjDetails.fsnmTable))); 

            fskupdTable = tableObjDetails.fskupdTableProper; 
            rect = tableObjDetails.fsrcTableObj; 

            PTS.Validate(PTS.FsQueryTableObjTableProperDetails( 
                PtsContext.Context,
                tableObjDetails.pfstableProper,
                out tableDetails));
 
            if (tableDetails.cRows == 0)
            { 
                //  table has no rows (thus no content) 
                arrayTableRowDesc = null;
                return (false); 
            }

            arrayTableRowDesc = new PTS.FSTABLEROWDESCRIPTION[tableDetails.cRows];
 
            fixed (PTS.FSTABLEROWDESCRIPTION * rgTableRowDesc = arrayTableRowDesc)
            { 
                int cRowsActual; 

                PTS.Validate(PTS.FsQueryTableObjRowList( 
                    PtsContext.Context,
                    tableObjDetails.pfstableProper,
                    tableDetails.cRows,
                    rgTableRowDesc, 
                    out cRowsActual));
 
                Debug.Assert(tableDetails.cRows == cRowsActual); 
            }
 
            return (true);
        }

        ///  
        /// Queries PTS and returns detailed structure / layout information about cells.
        ///  
        /// Row to query 
        /// Names of the cells
        /// Update state of the cells 
        /// Merge state of the cells
        /// 
        /// Critical - as this calls Critical functions PTS.FsQueryTableObjRowDetails
        ///            and PTS.FsQueryTableObjCellList. 
        /// 
        [SecurityCritical] 
        private unsafe void QueryRowDetails( 
            IntPtr pfstablerow,
            out IntPtr[] arrayFsCell, 
            out PTS.FSKUPDATE[] arrayUpdate,
            out PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge)
        {
            PTS.FSTABLEROWDETAILS tableRowDetails; 

            PTS.Validate(PTS.FsQueryTableObjRowDetails( 
                PtsContext.Context, 
                pfstablerow,
                out tableRowDetails)); 

            arrayUpdate = new PTS.FSKUPDATE[tableRowDetails.cCells];
            arrayFsCell = new IntPtr[tableRowDetails.cCells];
            arrayTableCellMerge = new PTS.FSTABLEKCELLMERGE[tableRowDetails.cCells]; 

            if (tableRowDetails.cCells > 0) 
            { 
                fixed (PTS.FSKUPDATE * rgUpdate = arrayUpdate)
                { 
                    fixed (IntPtr * rgFsCell = arrayFsCell)
                    {
                        fixed (PTS.FSTABLEKCELLMERGE * rgTableCellMerge = arrayTableCellMerge)
                        { 
                            int cCellsActual;
 
                            PTS.Validate(PTS.FsQueryTableObjCellList( 
                                PtsContext.Context,
                                pfstablerow, 
                                tableRowDetails.cCells,
                                rgUpdate,
                                rgFsCell,
                                rgTableCellMerge, 
                                out cCellsActual));
 
                            Debug.Assert(tableRowDetails.cCells == cCellsActual); 
                        }
                    } 
                }
            }
        }
 
        /// 
        /// SynchronizeRowVisualsCollection. 
        ///  
        /// Collection to synchromize.
        /// Index to start syncronization from. 
        /// Row that still alive.
        private void SynchronizeRowVisualsCollection(
            VisualCollection rowVisualsCollection,
            int firstIndex, 
            TableRow row)
        { 
            //  quick check to see if collection is already synchronized 
            if (((RowVisual)(rowVisualsCollection[firstIndex])).Row != row)
            { 
                int lastIndex = firstIndex;
                int count = rowVisualsCollection.Count;

                while (++lastIndex < count) 
                {
                    RowVisual rowVisual = (RowVisual)(rowVisualsCollection[lastIndex]); 
                    if (rowVisual.Row == row)   break; 
                }
 
                rowVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex);
            }
        }
 
        /// 
        /// SynchronizeCellVisualsCollection. 
        ///  
        /// Collection to synchromize.
        /// Index to start syncronization from. 
        /// Visual that still alive.
        private void SynchronizeCellVisualsCollection(
            VisualCollection cellVisualsCollection,
            int firstIndex, 
            Visual visual)
        { 
            //  quick check to see if collection is already synchronized 
            if ((cellVisualsCollection[firstIndex]) != visual)
            { 
                int lastIndex = firstIndex;
                int count = cellVisualsCollection.Count;

                while (++lastIndex < count) 
                {
                    if (cellVisualsCollection[lastIndex] == visual) break; 
                } 

                cellVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex); 
            }
        }

        ///  
        /// Validates the row's visual.
        ///  
        /// Row Visual to validate. 
        /// Row to query.
        /// Row's change state. 
        /// Columns offsets information.
        /// 
        /// Critical - as this calls Critical function QueryRowDetails and passes
        ///            pfstablerow directly. 
        /// 
        [SecurityCritical] 
        private void ValidateRowVisualSimple( 
            RowVisual rowVisual,
            IntPtr pfstablerow, 
            PTS.FSKUPDATE fskupdRow,
            CalculatedColumn[] calculatedColumns)
        {
            PTS.FSKUPDATE[] arrayUpdate; 
            IntPtr[] arrayFsCell;
            PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; 
            VisualCollection cellVisualsCollection; 
            int sourceIndex;
 
            QueryRowDetails(
                pfstablerow,
                out arrayFsCell,
                out arrayUpdate, 
                out arrayTableCellMerge);
 
            cellVisualsCollection = rowVisual.Children; 
            sourceIndex = 0;
 
            for (int iC = 0; iC < arrayFsCell.Length; ++iC)
            {
                CellParaClient cellParaClient;
                double urCellOffset; 
                PTS.FSKUPDATE fskupdCell;
 
                if (    //  paginated case - cell may be null 
                        arrayFsCell[iC] == IntPtr.Zero
                        //  exclude hanging cells 
                    ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle
                    ||  arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast   )
                {
                    continue; 
                }
 
                fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited) 
                    ? arrayUpdate[iC]
                    : fskupdRow; 

                if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange)
                {
                    cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                    urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset;
 
                    cellParaClient.ValidateVisual(); 

                    if (    fskupdCell == PTS.FSKUPDATE.fskupdNew 
                        //  PTS bug is a suspect here - this is a temp workaround:
                        ||  VisualTreeHelper.GetParent(cellParaClient.Visual) == null   )
                    {
                        Visual currentParent = VisualTreeHelper.GetParent(cellParaClient.Visual) as Visual; 
                        if(currentParent != null)
                        { 
                            ContainerVisual parent = currentParent as ContainerVisual; 
                            Invariant.Assert(parent != null, "parent should always derives from ContainerVisual");
                            parent.Children.Remove(cellParaClient.Visual); 
                        }
                        cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual);
                    }
                    else 
                    {
                        Debug.Assert(   cellParaClient.Visual != null 
                                    //  If the check below fails, then PTS cheats by reporting "ChangInside" for 
                                    //  a cell that in fact was re-Formatted.
                                    &&  VisualTreeHelper.GetParent(cellParaClient.Visual) != null   ); 

                        SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual);
                    }
                } 
                sourceIndex++;
            } 
 
            if (cellVisualsCollection.Count > sourceIndex)
            { 
                cellVisualsCollection.RemoveRange(
                    sourceIndex,
                    cellVisualsCollection.Count - sourceIndex);
            } 

            #if DEBUGDEBUG 
            for (int iC = 0, sourceIndex = 0; iC < arrayFsCell.Length; ++iC) 
            {
                if (    arrayFsCell[iC] != IntPtr.Zero 
                    &&  arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle
                    &&  arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast   )
                {
                    CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                    Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex);
                    sourceIndex++; 
                } 
            }
            #endif // DEBUGDEBUG 
        }

        /// 
        /// Validates the row's visual. 
        /// 
        /// Row Visual to validate. 
        /// Row to query. 
        /// Number of columns in the table.
        /// Row's change state. 
        /// Columns offsets information.
        /// 
        ///
        ///  
        /// 
        /// Critical - as this calls Critical function QueryRowDetails and passes it 
        ///            pfstablerow directly. 
        /// 
        [SecurityCritical] 
        private void ValidateRowVisualComplex(
            RowVisual rowVisual,
            IntPtr pfstablerow,
            int tableColumnCount, 
            PTS.FSKUPDATE fskupdRow,
            CalculatedColumn[] calculatedColumns) 
        { 
            PTS.FSKUPDATE[] arrayUpdate;
            IntPtr[] arrayFsCell; 
            PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge;
            CellParaClientEntry[] arrayCellParaClients;
            VisualCollection cellVisualsCollection;
            int sourceIndex; 

            QueryRowDetails( 
                pfstablerow, 
                out arrayFsCell,
                out arrayUpdate, 
                out arrayTableCellMerge);

            //  arrayFsCell lists cells in order different from one we want to maintain in visual collection.
            //  before going to update visual collection it is necessary to reorder ascending by cell's column index. 
            //  knowing the following facts:
            //  * total number of columns the row holds (including row spanned cells from previous rows) less or equal to tableColumnCount; 
            //  * total number of cells (including row spanned cells from previous rows) is less or equal to tableColumnCount; 
            //  * cells do not overlap - no two cells have the same column index;
            //  * cells' column indices fall into the range [0, tableColumnCount - 1]; 
            //  it is possible to write custom and optimized sorting routine:
            //  * iterate through arrayFsCell;
            //  * for each item record its CellParaClient value into arrayCellParaClients[CellParaClient.ColumnIndex];
            //  once complete, arrayCellParaClients will contain CellParaClients in correct order. some entries however will be null 
            //  due to potential column spanning of cells.
            arrayCellParaClients = new CellParaClientEntry[tableColumnCount]; 
 
            for (int iC = 0; iC < arrayFsCell.Length; ++iC)
            { 
                CellParaClient cellParaClient;
                PTS.FSKUPDATE fskupdCell;
                int columnIndex;
 
                if (arrayFsCell[iC] == IntPtr.Zero)
                { 
                    //  paginated case - cell may be null 
                    continue;
                } 

                fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited)
                    ? arrayUpdate[iC]
                    : fskupdRow; 

                cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); 
                columnIndex = cellParaClient.ColumnIndex; 
                arrayCellParaClients[columnIndex].cellParaClient = cellParaClient;
                arrayCellParaClients[columnIndex].fskupdCell = fskupdCell; 
            }

            cellVisualsCollection = rowVisual.Children;
            sourceIndex = 0; 

            for (int columnIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) 
            { 
                CellParaClient cellParaClient;
                double urCellOffset; 
                PTS.FSKUPDATE fskupdCell;

                cellParaClient = arrayCellParaClients[columnIndex].cellParaClient;
                if (cellParaClient == null) 
                {
                    //  paginated case - cell may be null 
                    continue; 
                }
 
                fskupdCell = arrayCellParaClients[columnIndex].fskupdCell;

                if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange)
                { 
                    urCellOffset = calculatedColumns[columnIndex].UrOffset;
                    cellParaClient.ValidateVisual(); 
 
                    if (fskupdCell == PTS.FSKUPDATE.fskupdNew)
                    { 
                        cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual);
                    }
                    else
                    { 
                        Debug.Assert(   cellParaClient.Visual != null
                                    //  If the check below fails, then PTS cheats by reporting "ChangInside" for 
                                    //  a cell that in fact was re-Formatted. 
                                    &&  VisualTreeHelper.GetParent(cellParaClient.Visual) != null   );
 
                        SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual);
                    }
                }
                sourceIndex++; 
            }
 
            if (cellVisualsCollection.Count > sourceIndex) 
            {
                cellVisualsCollection.RemoveRange( 
                    sourceIndex,
                    cellVisualsCollection.Count - sourceIndex);
            }
 
            #if DEBUGDEBUG
            for (int columnIndex = 0, sourceIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) 
            { 
                CellParaClient cellParaClient = arrayCellParaClients[columnIndex].cellParaClient;
                if (cellParaClient != null) 
                {
                    Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex);
                    sourceIndex++;
                } 
            }
            #endif // DEBUGDEBUG 
        } 

        ///  
        /// Draws the backgrounds for all columns in the table
        /// 
        /// Drawing Context.
        /// Content rect of table. 
        private void DrawColumnBackgrounds(DrawingContext dc, Rect tableContentRect)
        { 
            double columnStart = tableContentRect.X; 
            Rect colRect = tableContentRect;
            Brush columnBackgroundBrush; 

            if (ThisFlowDirection != PageFlowDirection)
            {
                for (int iC = CalculatedColumns.Length-1; iC >= 0; iC--) 
                {
                    columnBackgroundBrush = (iC < Table.Columns.Count) 
                        ? Table.Columns[iC].Background 
                        : null;
 
                    colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing;

                    if (columnBackgroundBrush != null)
                    { 
                        colRect.X = columnStart;
                        dc.DrawRectangle(columnBackgroundBrush, null, colRect); 
                    } 

                    columnStart += colRect.Width; 
                }
            }
            else
            { 
                for (int iC = 0; iC < CalculatedColumns.Length; iC++)
                { 
                    columnBackgroundBrush = (iC < Table.Columns.Count) 
                        ? Table.Columns[iC].Background
                        : null; 

                    colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing;

                    if (columnBackgroundBrush != null) 
                    {
                        colRect.X = columnStart; 
                        dc.DrawRectangle(columnBackgroundBrush, null, colRect); 
                    }
 
                    columnStart += colRect.Width;
                }
            }
        } 

        ///  
        /// Calculates actual row height, subtracting what was previously reported as dvrBeforeTopRow and dvrAfterBottomRow 
        /// 
        private double GetActualRowHeight(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, int rowIndex, MbpInfo mbpInfo) 
        {
            int dvAdjustment = 0;

            if(IsFirstChunk && rowIndex == 0) 
            {
                dvAdjustment = -mbpInfo.BPTop; 
            } 

            if(IsLastChunk && rowIndex == arrayTableRowDesc.Length - 1) 
            {
                dvAdjustment = -mbpInfo.BPBottom;
            }
 
            return TextDpi.FromTextDpi(arrayTableRowDesc[rowIndex].u.dvrRow + dvAdjustment);
        } 
 
        /// 
        /// Draws TableRowGroup backgrounds 
        /// 
        /// DrawingContext to draw in.
        /// Row descriptions.
        /// External rect for this chunk of table. 
        /// Margin/Border/Padding info for table.
        ///  
        /// 
        /// 
        ///  
        /// Critical - as this uses unmanaged data structures with a union
        /// 
        [SecurityCritical]
        private void DrawRowGroupBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo) 
        {
            double rowGroupTop = tableContentRect.Y; 
            double rowGroupHeight = 0; 
            Rect rowRect = tableContentRect;
            Brush rowGroupBackgroundBrush; 

            if(arrayTableRowDesc.Length > 0)
            {
                TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow))).Row; 
                TableRowGroup tableRowGroup = row.RowGroup;
 
                for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) 
                {
                    row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; 

                    if (tableRowGroup != row.RowGroup)
                    {
                        rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty); 

                        if (rowGroupBackgroundBrush != null) 
                        { 
                            rowRect.Y = rowGroupTop;
                            rowRect.Height = rowGroupHeight; 
                            dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect);
                        }

                        rowGroupTop += rowGroupHeight; 
                        tableRowGroup = row.RowGroup;
                        rowGroupHeight = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); 
                    } 
                    else
                    { 
                        rowGroupHeight += GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo);
                    }
                }
 
                rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty);
                if (rowGroupBackgroundBrush != null) 
                { 
                    rowRect.Y = rowGroupTop;
                    rowRect.Height = rowGroupHeight; 
                    dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect);
                }
            }
 
        }
 
        ///  
        /// Draws TableRow backgrounds
        ///  
        /// DrawingContext to draw in.
        /// Row descriptions.
        /// External rect for this chunk of table.
        /// Margin/Border/Padding info for table. 
        /// 
        /// 
        ///  
        /// 
        /// Critical - as this uses unmanaged data structures with a union 
        /// 
        [SecurityCritical]
        private void DrawRowBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo)
        { 
            double rowTop = tableContentRect.Y;
            Rect rowRect = tableContentRect; 
 
            for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR)
            { 
                TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row;
                Brush rowBackgroundBrush = (Brush)row.GetValue(TextElement.BackgroundProperty);

                rowRect.Y = rowTop; 
                rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo);
 
                if (rowBackgroundBrush != null) 
                {
                    dc.DrawRectangle(rowBackgroundBrush, null, rowRect); 
                }

                rowTop += rowRect.Height; // Adjust for top of next row
            } 
        }
 
        ///  
        /// ValidateCalculatedColumns
        ///  
        /// 
        /// Side effect: _durMinWidth and _durMaxWidth are also updated.
        /// 
        private void ValidateCalculatedColumns() 
        {
            double totalPadding; 
            int columns = Table.ColumnCount; 

            if (_calculatedColumns == null) 
            {
                _calculatedColumns = new CalculatedColumn[columns];
            }
            else if (_calculatedColumns.Length != columns) 
            {
                CalculatedColumn[] newCalculatedColumns = new CalculatedColumn[columns]; 
 
                Array.Copy(
                    _calculatedColumns, 
                    newCalculatedColumns,
                    Math.Min(_calculatedColumns.Length, columns));

                _calculatedColumns = newCalculatedColumns; 
            }
 
            if (_calculatedColumns.Length > 0) 
            {
                int i = 0; 

                while (i < _calculatedColumns.Length && i < Table.Columns.Count)
                {
                    _calculatedColumns[i].UserWidth = Table.Columns[i].Width; 
                    i++;
                } 
 
                while (i < _calculatedColumns.Length)
                { 
                    _calculatedColumns[i].UserWidth = TableColumn.DefaultWidth;
                    i++;
                }
            } 

            _durMinWidth = _durMaxWidth = 0; 
 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            { 
                switch (_calculatedColumns[i].UserWidth.GridUnitType)
                {
                    case (GridUnitType.Auto):
                        // 

                        _calculatedColumns[i].ValidateAuto(1.0, 10e5); 
                        break; 

                    case (GridUnitType.Star): 
                        _calculatedColumns[i].ValidateAuto(1.0, 10e5);
                        break;

                    case (GridUnitType.Pixel): 
                        _calculatedColumns[i].ValidateAuto(
                            _calculatedColumns[i].UserWidth.Value, 
                            _calculatedColumns[i].UserWidth.Value); 
                        break;
 
                    default:
                        Debug.Assert(false, "Unsupported unit type");
                        break;
                } 
                _durMinWidth += _calculatedColumns[i].DurMinWidth;
                _durMaxWidth += _calculatedColumns[i].DurMaxWidth; 
            } 

            // Use durAvailable as width limit for MBP, and MaxWidth as height limit since height values will not be used 
            MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element);

            totalPadding = Table.InternalCellSpacing * Table.ColumnCount + mbpInfo.Margin.Left + mbpInfo.Border.Left + mbpInfo.Padding.Left + mbpInfo.Padding.Right + mbpInfo.Border.Right + mbpInfo.Margin.Right;
            _durMinWidth += totalPadding; 
            _durMaxWidth += totalPadding;
        } 
 
        /// 
        /// ValidateTableWidths 
        /// 
        /// Availble width
        /// Resulting table width
        ///  
        /// PTS.True when no changes in columns width detected,
        /// PTS.False otherwise 
        ///  
        private int ValidateTableWidths(double durAvailableWidth, out double durTableWidth)
        { 
            bool exactWidth = false;                //  assume that width should never be "exact"
            double durAbsoluteMin, durAbsoluteMax;  //  sum of min and max for all user absolute specified columns
            double durScalableMin;                  //  sum if min for all user percent specified columns
            double durAutoMin, durAutoMax;          //  sum of min and max for all non-specified columns 
            double mul, div, fP;
            double iPercent, iP; 
            double durAbsoluteWidths;               //  calculated sum of widths for all absolute columns 
            double durAbsoluteAndAutoWidths;        //  calculated sum of widths for all non-scalable columns
            double durAutoWidths;                   //  calculated sum of widths for all non-specified columns 
            bool fUseMax = false, fUseMin = false, fUseMaxMax = false;
            bool fUseUserMax = false, fUseUserMin = false, fUseUserMaxMax = false;
            bool fSubtract = false, fUserSubtract = false;
            double durTableUserWidth; 
            double cellSpacing = Table.InternalCellSpacing;
            // Use durAvailable for MBP width limits and MaxWidth for height limits. Height values are not used in this calculation. 
            MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element); 

            double durTotalPadding = cellSpacing * Table.ColumnCount + TextDpi.FromTextDpi(mbpInfo.MBPLeft + mbpInfo.MBPRight); 
            int ptsNoWidthChanges = PTS.True;

            // initialize output
            durTableWidth = 0; 

            // 
            // first calc sum of known percent, absolute and auto widths up 
            //
            iPercent = 0; 
            durAbsoluteMin = durAbsoluteMax = 0;
            durScalableMin = 0;
            durAutoMin = durAutoMax = 0;
 
            //
            // also keep track of the maxWidth/percent ratio necessary to 
            // display the table with the columns at max width and the right 
            // percent value
            // 

            // Step 1 - Run through all of our calculated columns, and determine min and max widths (these widths do NOT include cell spacing)
            mul = 0;
            div = 1; 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            { 
                Debug.Assert(_calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Auto || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Star || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Pixel, "Unexpected GridUnitType"); 
                if (_calculatedColumns[i].UserWidth.IsAuto)
                { 
                    Debug.Assert(0 <= _calculatedColumns[i].DurMinWidth && 0 <= _calculatedColumns[i].DurMaxWidth);
                    durAutoMin += _calculatedColumns[i].DurMinWidth;
                    durAutoMax += _calculatedColumns[i].DurMaxWidth;
                } 
                else
                { 
                    if (_calculatedColumns[i].UserWidth.IsStar) 
                    {
                        iP = _calculatedColumns[i].UserWidth.Value; 
                        if (iP < 0)
                        {
                            iP = 0;
                        } 

                        // check for over 100% overflow 
                        if (iPercent + iP > 100) 
                        {
                            iP = 100 - iPercent; 
                            iPercent = 100;
                            _calculatedColumns[i].UserWidth = new GridLength(iP, GridUnitType.Star);
                        }
                        else 
                        {
                            iPercent += iP; 
                        } 

                        if (iP == 0) 
                        {
                            // non empty cell should get at least 1%
                            iP = 1;
                        } 

                        // remember maxWidth/percentage ratio 
                        if (_calculatedColumns[i].DurMaxWidth * div > iP * mul) 
                        {
                            mul = _calculatedColumns[i].DurMaxWidth; 
                            div = iP;
                        }

                        durScalableMin += _calculatedColumns[i].DurMinWidth; 
                    }
                    else 
                    { 
                        durAbsoluteMin += _calculatedColumns[i].DurMinWidth;
                        durAbsoluteMax += _calculatedColumns[i].DurMaxWidth; 
                    }
                }
            }
 
            // iP is what left from the 100%
 
            // Step 2 - Calculate table user width - This width includes all padding and is a percentage of durAvailableWidth or _durMinWidth / _durMaxWidth, 
            // all three values include padding.
 
            iP = 100 - iPercent;
            Debug.Assert(0 < iP || DoubleUtil.IsZero(iP));
            if (exactWidth)
            { 
                // have no choice - table's parent requires the width to be exact
                durTableUserWidth = durAvailableWidth; 
                if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) 
                {
                    durTableUserWidth = _durMinWidth; 
                }
            }
            else if (0 < iPercent)  // if user width is not given back calculate it from the maxWidth/percentage ratio
            { 
                // check if the remaining user and normal columns are requiring bigger ratio
                if (0 < iP) 
                { 
                    double tl = (durAbsoluteMax + durAutoMax) * div;
                    double tr = iP * mul; 

                    // floating point (tl > tr)
                    if (tl > tr && DoubleUtil.AreClose(tl, tr) == false)
                    { 
                        mul = durAbsoluteMax + durAutoMax;
                        div = iP; 
                    } 
                }
 
                //
                // if there is percentage left or there are only percentage columns use the ratio
                // to back-calculate the table width
                // 
                if ((0 < iP) || DoubleUtil.IsZero(durAbsoluteMax + durAutoMax))
                { 
                    durTableUserWidth = mul * 100 / div + durTotalPadding; 

                    // floating point (durTableUserWidth > durAvailableWidth) 
                    if (durTableUserWidth > durAvailableWidth && DoubleUtil.AreClose(durTableUserWidth, durAvailableWidth) == false)
                    {
                        durTableUserWidth = durAvailableWidth;
                    } 
                }
                else 
                { 
                    // otherwise use available width
                    durTableUserWidth = durAvailableWidth; 
                }

                // floating point (durTableUserWidth < _durMinWidth)
                if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) 
                {
                    durTableUserWidth = _durMinWidth; 
                } 
            }
            else 
            {
                // floating point (_durMaxWidth < durAvailableWidth)
                if (_durMaxWidth < durAvailableWidth && DoubleUtil.AreClose(_durMaxWidth, durAvailableWidth) == false)
                { 
                    // use max value if that smaller the parent size
                    durTableUserWidth = _durMaxWidth; 
                } 
                // floating point (_durMinWidth > durAvailableWidth)
                else if (_durMinWidth > durAvailableWidth && DoubleUtil.AreClose(_durMinWidth, durAvailableWidth) == false) 
                {
                    // have to use min if that is bigger the parent
                    durTableUserWidth = _durMinWidth;
                } 
                else
                { 
                    // use parent between min and max 
                    durTableUserWidth = durAvailableWidth;
                } 
            }

            // subtract padding width which contains border and cellspacing
            // floating point (durTableUserWidth >= durTotalPadding) 

            // Step 3 - Remove padding - ( 
            if (durTableUserWidth > durTotalPadding || DoubleUtil.AreClose(durTableUserWidth, durTotalPadding)) 
            {
                durTableUserWidth -= durTotalPadding; 
            }

            // floating point (0 < (durAutoMax + durAbsoluteMax))
            // Step 4 - Calculate the space for 'auto' sized columns. 
            if (0 < (durAutoMax + durAbsoluteMax) && DoubleUtil.IsZero(durAutoMax + durAbsoluteMax) == false)
            { 
                // cache width remaining for normal and user columns over percent columns (durAbsoluteAndAutoWidths) 
                durAbsoluteAndAutoWidths = iP * durTableUserWidth / 100;
 
                // floating point (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin))
                if (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durAbsoluteMin + durAutoMin)) == false)
                {
                    durAbsoluteAndAutoWidths = durAbsoluteMin + durAutoMin; 
                }
 
                // floating point (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin)) 
                if (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durTableUserWidth - durScalableMin)) == false)
                { 
                    durAbsoluteAndAutoWidths = durTableUserWidth - durScalableMin;
                }
            }
            else 
            {
                // all widths is for percent columns 
                durAbsoluteAndAutoWidths = 0; 
            }
 
            //
            // distribute remaining width amongst normal and user columns
            // first try to use max width for user columns and normal columns
            // 
            // floating point (0 < durAbsoluteMax)
            if (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) 
            { 
                durAbsoluteWidths = durAbsoluteMax;
                if (durAbsoluteWidths > durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) 
                {
                    durAbsoluteWidths = durAbsoluteAndAutoWidths;
                }
 
                // floating point (0 < durAutoMax)
                if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                { 
                    durAutoWidths = durAutoMin;
 
                    // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths)
                    if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths))
                    {
                        durAutoWidths = durAbsoluteAndAutoWidths - durAbsoluteWidths; 
                    }
                    else 
                    { 
                        durAbsoluteWidths = durAbsoluteMin;
 
                        // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths)
                        if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths))
                        {
                            durAbsoluteWidths = durAbsoluteAndAutoWidths - durAutoWidths; 
                        }
                    } 
                } 
                else
                { 
                    durAutoWidths = 0;

                    // floating point (durAbsoluteWidths < durAbsoluteAndAutoWidths)
                    if (durAbsoluteWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) 
                    {
                        durAbsoluteWidths = durAbsoluteAndAutoWidths; 
                    } 
                }
            } 
            else
            {
                durAbsoluteWidths = 0;
 
                // floating point (0 < durAutoMax)
                if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                { 
                    durAutoWidths = durAutoMin;
 
                    // floating point (durAutoWidths < durAbsoluteAndAutoWidths)
                    if (durAutoWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAutoWidths, durAbsoluteAndAutoWidths) == false)
                    {
                        durAutoWidths = durAbsoluteAndAutoWidths; 
                    }
                } 
                else 
                {
                    durAutoWidths = 0; 
                }
            }

            // floating point (durAutoWidths > durAutoMax) 
            if (durAutoWidths > durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false)
            { 
                fUseMaxMax = true; 
            }
            // floating point (durAutoWidths == durAutoMax) 
            else if (DoubleUtil.AreClose(durAutoWidths, durAutoMax))
            {
                fUseMax = true;
            } 
            // floating point (durAutoWidths == durAutoMin)
            else if (DoubleUtil.AreClose(durAutoWidths, durAutoMin)) 
            { 
                fUseMin = true;
            } 
            // floating point (durAutoWidths < durAutoMax)
            else if (durAutoWidths < durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false)
            {
                fSubtract = true; 
            }
 
            // floating point (durAbsoluteWidths > durAbsoluteMax) 
            if (durAbsoluteWidths > durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false)
            { 
                fUseUserMaxMax = true;
            }
            // floating point (durAbsoluteWidths == durAbsoluteMax)
            else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax)) 
            {
                fUseUserMax = true; 
            } 
            // floating point (durAbsoluteWidths == durAbsoluteMin)
            else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMin)) 
            {
                fUseUserMin = true;
            }
            // floating point (durAbsoluteWidths < durAbsoluteMax) 
            else if (durAbsoluteWidths < durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false)
            { 
                fUserSubtract = true; 
            }
 
            // calculate real percentage of percent columns in the table now using the final widths
            fP = (0 < durTableUserWidth) ? (100 * (durTableUserWidth - durAbsoluteWidths - durAutoWidths) / durTableUserWidth) : 0;

            // start with content area of table 
            durTableWidth = TextDpi.FromTextDpi(mbpInfo.BPLeft);
            // 
            // now go and calculate column widths by distributing the extra width over 
            // the min width or subtracting the extra width from max
            // 
            for (int i = 0; i < _calculatedColumns.Length; ++i)
            {
                if (_calculatedColumns[i].UserWidth.IsAuto)
                { 
                    // adjust normal column by adding to min or subtracting from max
                    _calculatedColumns[i].DurWidth = 
                        fSubtract 
                        ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAutoMax - durAutoWidths) / (durAutoMax - durAutoMin))
                        : fUseMaxMax 
                        ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMax) / durAutoMax)
                        : fUseMax
                        ? _calculatedColumns[i].DurMaxWidth
                        : fUseMin 
                        ? _calculatedColumns[i].DurMinWidth
                          // floating point (0 < durAutoMax) 
                        : (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) 
                        ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMin) / durAutoMax)
                        : 0; 
                }
                else if (_calculatedColumns[i].UserWidth.IsStar)
                {
                    // 
                    // if percent first calculate the width from the percent
                    // 
                    durAbsoluteAndAutoWidths = 
                        (0 < iPercent)
                        ? (durTableUserWidth * (fP * _calculatedColumns[i].UserWidth.Value / iPercent) / 100) 
                        : 0;

                    //
                    // make sure it is over the min width 
                    //
                    durAbsoluteAndAutoWidths -= _calculatedColumns[i].DurMinWidth; 
 
                    // floating point (durAbsoluteAndAutoWidths < 0)
                    if (durAbsoluteAndAutoWidths < 0 && DoubleUtil.IsZero(durAbsoluteAndAutoWidths) == false) 
                    {
                        durAbsoluteAndAutoWidths = 0;
                    }
 
                    _calculatedColumns[i].DurWidth = (_calculatedColumns[i].DurMinWidth + durAbsoluteAndAutoWidths);
                } 
                else 
                {
                    Debug.Assert(_calculatedColumns[i].UserWidth.IsAbsolute); 

                    // adjust user column by adding to min or subtracting from max
                    _calculatedColumns[i].DurWidth =
                        fUserSubtract 
                        // table needs to be (durAbsoluteMax - durAbsoluteWidths) shorter, so subtract width
                        // from columns proportionally to (_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) 
                        ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAbsoluteMax - durAbsoluteWidths) / (durAbsoluteMax - durAbsoluteMin)) 
                        : fUseUserMaxMax
                        ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMax) / durAbsoluteMax) 
                        : fUseUserMax
                        ? _calculatedColumns[i].DurMaxWidth
                        : fUseUserMin
                        ? _calculatedColumns[i].DurMinWidth 
                        // floating point (0 < durAbsoluteMax)
                        : (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) 
                        ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMin) / durAbsoluteMax) 
                        : 0;
                } 

                Debug.Assert(_calculatedColumns[i].DurMinWidth <= _calculatedColumns[i].DurMaxWidth);
                // Cells themselves are 1/2 cell spacing inside column
                _calculatedColumns[i].UrOffset = durTableWidth + cellSpacing / 2.0; 

                durTableWidth += _calculatedColumns[i].DurWidth + cellSpacing; // Advance to next column 
 
                if (_calculatedColumns[i].PtsWidthChanged == PTS.True)
                { 
                    ptsNoWidthChanges = PTS.False;
                }
            }
 
            durTableWidth += mbpInfo.Margin.Left + TextDpi.FromTextDpi(mbpInfo.MBPRight);
 
            return (ptsNoWidthChanges); 
        }
 
        /// 
        /// Content rect - Rect from which rows/rowgroups/columns are calculated.
        /// 
        private PTS.FSRECT GetTableContentRect(MbpInfo mbpInfo) 
        {
            int calculatedBPTop = IsFirstChunk ? mbpInfo.BPTop : 0; 
            int calculatedBPBottom = IsLastChunk ? mbpInfo.BPBottom : 0; 

            return new PTS.FSRECT(_rect.u + mbpInfo.BPLeft, 
                                  _rect.v + calculatedBPTop,
                                  Math.Max(_rect.du - (mbpInfo.BPRight + mbpInfo.BPLeft), 1),
                                  Math.Max(_rect.dv - calculatedBPBottom - calculatedBPTop, 1)
                                 ); 
        }
 
        ///  
        /// Returns the offset from the table top to the first row of the table for the current chunk.
        ///  
        private int GetTableOffsetFirstRowTop()
        {
            MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element);
 
            return IsFirstChunk ? mbp.BPTop : 0;
        } 
 
        #endregion Private Methods
 
        //-----------------------------------------------------
        //
        //  Private Classes / Structures
        // 
        //------------------------------------------------------
 
        #region Private Classes / Structures 

        private struct CellParaClientEntry 
        {
            internal CellParaClient cellParaClient;
            internal PTS.FSKUPDATE fskupdCell;
        } 

        #endregion Private Classes / Structures 
 
        //-----------------------------------------------------
        // 
        //  Private Data
        //
        //------------------------------------------------------
 
        #region Private Data
 
        // Rectangle of column table para client is in. 
        private PTS.FSRECT _columnRect;
        private CalculatedColumn[] _calculatedColumns;  //  layout related information per column 

        private double _durMinWidth;                    //  minimum width of the table
        private double _durMaxWidth;                    //  maximum wudth of the table
 
        private double _previousAutofitWidth = 0.0;     // Autofit width on previous layout pass.
        private double _previousTableWidth = 0.0;       // Table width on previous layout pass. 
 
        #endregion Private Data
    } 
}


// 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