private void PreCoalesceRow(DocumentNode dn, ref bool fVMerged)
        {
            DocumentNodeArray dnaCells = dn.GetRowsCells();
            RowFormat rf = dn.FormatState.RowFormat;
            DocumentNode dnTable = dn.GetParentOfType(DocumentNodeType.dnTable);
            ColumnStateArray csa = (dnTable != null) ? dnTable.ColumnStateArray : null;

            // Normally number of cells and cell definitions are equal, but be careful.
            int nCount = dnaCells.Count < rf.CellCount ? dnaCells.Count : rf.CellCount;

            // Non-unary colspan can arise both because I have "merged" cells specified
            // as well as because I just have cells that exactly span some other cols.
            // The code in PreCoalesce enforces that the cells line up, so I can just
            // test for that here.

            int nColsSeen = 0;
            int i = 0;
            while (i < nCount)
            {
                DocumentNode dnCell = dnaCells.EntryAt(i);
                CellFormat cf = rf.NthCellFormat(i);
                long cellx = cf.CellX;

                // optimization - record if we encountered a vmerged cell
                if (cf.IsVMerge)
                {
                    fVMerged = true;
                }

                // Determine colspan based on cells we will eliminate through the merge flags
                if (cf.IsHMergeFirst)
                {
                    for (i++; i < nCount; i++)
                    {
                        cf = rf.NthCellFormat(i);
                        if (cf.IsVMerge)
                        {
                            fVMerged = true;
                        }
                        if (cf.IsHMerge)
                        {
                            dnaCells.EntryAt(i).ColSpan = 0;    // zero means omit this cell
                        }
                    }
                }
                else
                {
                    i++;
                }

                // Determine actual colspan based on cellx value
                if (csa != null)
                {
                    int nColStart = nColsSeen;

                    while (nColsSeen < csa.Count)
                    {
                        ColumnState cs = csa.EntryAt(nColsSeen);
                        nColsSeen++;

                        // This is the normal case
                        if (cs.CellX == cellx)
                        {
                            break;
                        }

                        // This is anomalous, but can occur with odd \cellx values (non-monotonically increasing).
                        if (cs.CellX > cellx)
                        {
                            break;
                        }
                    }

                    if (nColsSeen - nColStart > dnCell.ColSpan)
                    {
                        dnCell.ColSpan = nColsSeen - nColStart;
                    }
                }
            }
        }
        private void WriteRowsCellProperties(DocumentNode dnRow) 
        {
            DocumentNodeArray cellArray = dnRow.GetRowsCells(); 

            int nCol = 0;
            long lastCellX = 0;
 
            for (int i = 0; i < cellArray.Count; i++)
            { 
                DocumentNode dnCell = cellArray.EntryAt(i); 

                lastCellX = WriteCellProperties(dnCell, nCol, lastCellX); 
                nCol += dnCell.ColSpan;
            }
        }
        private void WriteRowsCellContents(DocumentNode dnRow)
        { 
            DocumentNodeArray cellArray = dnRow.GetRowsCells(); 

            _rtfBuilder.Append("{"); 
            for (int i = 0; i < cellArray.Count; i++)
            {
                DocumentNode dnCell = cellArray.EntryAt(i);
 
                WriteStructure(dnCell);
            } 
            _rtfBuilder.Append("}"); 
        }
        private void WriteRowBorders(DocumentNode dnRow) 
        {
            // XAML doesn't have notion of default row borders, so there is no explicit attribute
            // in the XAML content to use here and in fact we will always override these values with
            // explicit cell border properties. 
            // However, so they are not nonsensical, pick the first cell's properties to write out and if
            // borders are uniform for the row, this will actually be accurate. 
            DocumentNodeArray cellArray = dnRow.GetRowsCells(); 
            if (cellArray.Count > 0)
            { 
                DocumentNode dnCell = cellArray.EntryAt(0);
                if (dnCell.FormatState.HasRowFormat)
                {
                    CellFormat cf = dnCell.FormatState.RowFormat.RowCellFormat; 

                    WriteBorder("\\trbrdrt", cf.BorderTop); 
                    WriteBorder("\\trbrdrb", cf.BorderBottom); 
                    WriteBorder("\\trbrdrr", cf.BorderRight);
                    WriteBorder("\\trbrdrl", cf.BorderLeft); 
                    WriteBorder("\\trbrdrv", cf.BorderLeft);
                    WriteBorder("\\trbrdrh", cf.BorderTop);
                }
            } 
        }