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 WriteStructure(DocumentNode dnThis) { DocumentNodeArray dna = _converterState.DocumentNodeArray; bool nested = dnThis.GetParentOfType(DocumentNodeType.dnCell) != null; // Prolog switch (dnThis.Type) { case DocumentNodeType.dnSection: { WriteSection(dnThis); return; } case DocumentNodeType.dnParagraph: { WriteParagraph(dnThis); return; } case DocumentNodeType.dnInline: { WriteInlineChild(dnThis); return; } case DocumentNodeType.dnInlineUIContainer: case DocumentNodeType.dnBlockUIContainer: { WriteUIContainerChild(dnThis); return; } case DocumentNodeType.dnList: case DocumentNodeType.dnListItem: // Handled as paragraph properties break; case DocumentNodeType.dnTable: // Make sure column format is canonicalized if (dnThis.FormatState.HasRowFormat) { dnThis.FormatState.RowFormat.Trleft = dnThis.FormatState.LI; dnThis.FormatState.RowFormat.CanonicalizeWidthsFromXaml(); } PatchVerticallyMergedCells(dnThis); break; case DocumentNodeType.dnTableBody: // For RTF, we only write row properties. break; case DocumentNodeType.dnRow: WriteRow(dnThis); break; case DocumentNodeType.dnCell: break; default: // Not really structure return; } // Write direct children, except for row if (dnThis.Type != DocumentNodeType.dnRow) { int nStart = dnThis.Index + 1; int nEnd = dnThis.Index + dnThis.ChildCount; for (; nStart <= nEnd; nStart++) { DocumentNode dnChild = dna.EntryAt(nStart); if (dnChild.Parent == dnThis) { WriteStructure(dnChild); } } } // Epilog switch (dnThis.Type) { case DocumentNodeType.dnList: case DocumentNodeType.dnListItem: // Handled as paragraph properties break; case DocumentNodeType.dnTable: break; case DocumentNodeType.dnTableBody: break; case DocumentNodeType.dnRow: // Handled above break; case DocumentNodeType.dnCell: if (!dnThis.IsTerminated) { _rtfBuilder.Append(nested ? "\\nestcell" : "\\cell"); _rtfBuilder.Append("\r\n"); } break; } }
/// <summary> /// Wirte the CellX control and value to layout the cell position on the table and /// return the last cell x position. /// There is no smart calculation for getting cell width without the specified value, /// so we use DefaultCellXAsTwips(1440) magic number which is the default CellX value on Word. /// </summary> private long WriteCellDimensions(DocumentNode dnCell, int nCol, long lastCellX) { DocumentNode dnTable = dnCell.GetParentOfType(DocumentNodeType.dnTable); if (dnTable.FormatState.HasRowFormat) { RowFormat rf = dnTable.FormatState.RowFormat; CellFormat cf = rf.NthCellFormat(nCol); if (dnCell.ColSpan > 1) { CellFormat cfSpanned = new CellFormat(cf); for (int i = 1; i < dnCell.ColSpan; i++) { cf = rf.NthCellFormat(nCol + i); cfSpanned.Width.Value += cf.Width.Value; cfSpanned.CellX = cf.CellX; } // Calculate the default value if CellX never set or has zero cell count if (cfSpanned.CellX == -1 || rf.CellCount == 0) { // Calculate the default CellX value with tables width cfSpanned.CellX = lastCellX + dnCell.ColSpan * DefaultCellXAsTwips + GetDefaultAllTablesWidthFromCell(dnCell); } // Write the encoded width information like as CellX control and value _rtfBuilder.Append(cfSpanned.RTFEncodingForWidth); // Save the last CellX value to accumulate it with the next cell lastCellX = cfSpanned.CellX; } else { if (cf.CellX == -1 || rf.CellCount == 0) { // Calculate the default CellX value cf.CellX = lastCellX + DefaultCellXAsTwips + GetDefaultAllTablesWidthFromCell(dnCell); } // Write the encoded width information like as CellX control and value _rtfBuilder.Append(cf.RTFEncodingForWidth); lastCellX = cf.CellX; } } else { _rtfBuilder.Append("\\clftsWidth1"); _rtfBuilder.Append("\\cellx"); // Set the CellX value and write the CellX control and value long cellX = lastCellX + dnCell.ColSpan * DefaultCellXAsTwips; _rtfBuilder.Append(cellX.ToString(CultureInfo.InvariantCulture)); lastCellX = cellX; } return lastCellX; }
private void WriteRowSettings(DocumentNode dnRow) { DocumentNode dnTable = dnRow.GetParentOfType(DocumentNodeType.dnTable); DirState dirHere = dnTable != null ? dnTable.XamlDir : DirState.DirLTR; DirState dirPa = dnTable != null ? dnTable.ParentXamlDir : DirState.DirLTR; if (dnTable != null) { // Note: Parent directionality determines margin interpretation. long l = dirPa == DirState.DirLTR ? dnTable.FormatState.LI : dnTable.FormatState.RI; string s = l.ToString(CultureInfo.InvariantCulture); _rtfBuilder.Append("\\trleft"); _rtfBuilder.Append(s); _rtfBuilder.Append("\\trgaph-"); _rtfBuilder.Append(s); } else { _rtfBuilder.Append("\\trgaph0"); _rtfBuilder.Append("\\trleft0"); } WriteRowBorders(dnRow); WriteRowDimensions(dnRow); WriteRowPadding(dnRow); _rtfBuilder.Append("\\trql"); if (dirHere == DirState.DirRTL) { _rtfBuilder.Append("\\rtlrow"); } else { _rtfBuilder.Append("\\ltrrow"); } }
private bool WriteParagraphListInfo(DocumentNode dnThis, FormatState fsThis) { bool bOutControl = false; bool bNewStyle = GenerateListTables; if (dnThis.ListLabel != null) { DocumentNode dnList = dnThis.GetParentOfType(DocumentNodeType.dnList); if (dnList != null) { // Old style list info for RichEdit and other non-Word client compat if I can. // Only do this for simple, non multi-level lists. if (bNewStyle && dnList.FormatState.PNLVL == 1) { bNewStyle = false; } if (bNewStyle) { _rtfBuilder.Append("{\\listtext "); _rtfBuilder.Append(dnThis.ListLabel); if (dnList.FormatState.Marker != MarkerStyle.MarkerBullet && dnList.FormatState.Marker != MarkerStyle.MarkerNone) { _rtfBuilder.Append("."); } _rtfBuilder.Append("\\tab}"); // NB: RichEdit requires \ls keyword to occur immediately after \listtext if (fsThis.ILS > 0) { _rtfBuilder.Append("\\ls"); _rtfBuilder.Append(fsThis.ILS.ToString(CultureInfo.InvariantCulture)); bOutControl = true; } if (fsThis.ILVL > 0) { _rtfBuilder.Append("\\ilvl"); _rtfBuilder.Append(fsThis.ILVL.ToString(CultureInfo.InvariantCulture)); bOutControl = true; } } else { _rtfBuilder.Append("{\\pntext "); _rtfBuilder.Append(dnThis.ListLabel); if (dnList.FormatState.Marker != MarkerStyle.MarkerBullet && dnList.FormatState.Marker != MarkerStyle.MarkerNone) { _rtfBuilder.Append("."); } _rtfBuilder.Append("\\tab}{\\*\\pn"); _rtfBuilder.Append(Converters.MarkerStyleToOldRTFString(dnList.FormatState.Marker)); if (fsThis.ListLevel > 0 && dnList.FormatState.PNLVL > 1) { _rtfBuilder.Append("\\pnlvl"); _rtfBuilder.Append(fsThis.ListLevel.ToString(CultureInfo.InvariantCulture)); } if (fsThis.FI > 0) { _rtfBuilder.Append("\\pnhang"); } if (fsThis.StartIndex >= 0) { _rtfBuilder.Append("\\pnstart"); _rtfBuilder.Append(fsThis.StartIndex.ToString(CultureInfo.InvariantCulture)); } if (dnList.FormatState.Marker == MarkerStyle.MarkerBullet) { _rtfBuilder.Append("{\\pntxtb\\'B7}}"); } else if (dnList.FormatState.Marker == MarkerStyle.MarkerNone) { _rtfBuilder.Append("{\\pntxta }{\\pntxtb }}"); } else { _rtfBuilder.Append("{\\pntxta .}}"); } // Already terminated with curly, no need for extra space. bOutControl = false; } } } return bOutControl; }
private void WriteParagraph(DocumentNode dnThis) { int nStart = dnThis.Index + 1; int nEnd = dnThis.Index + dnThis.ChildCount; int nAt; FormatState fsThis = dnThis.FormatState; FormatState fsParent = dnThis.Parent != null ? dnThis.Parent.FormatState : FormatState.EmptyFormatState; DocumentNodeArray dna = _converterState.DocumentNodeArray; _rtfBuilder.Append("{"); bool bOutControl = WriteParagraphFontInfo(dnThis, fsThis, fsParent); // Structure properties // NB: RE 4.01 seems to require \intbl keyword to come before inline content if (fsThis.IsInTable) { _rtfBuilder.Append("\\intbl"); bOutControl = true; } if (bOutControl) { _rtfBuilder.Append(" "); } bOutControl = WriteParagraphListInfo(dnThis, fsThis); if (bOutControl) { _rtfBuilder.Append(" "); } // FlowDirection control - state it before writing nested inline node. // MsWord expect "rtlpar" control before writing inline, but Wordpad // doesn't matter state it before or after of inline writing. if (fsThis.DirPara == DirState.DirRTL) { _rtfBuilder.Append("\\rtlpar"); } // OK, now write out the inline children. for (nAt = nStart; nAt <= nEnd; nAt++) { DocumentNode dnChild = dna.EntryAt(nAt); // Ignore non-direct children - they get written out by their parent if (dnChild.Parent == dnThis) { WriteInlineChild(dnChild); } } // Structure properties if (fsThis.ITAP > 1) { _rtfBuilder.Append("\\itap"); _rtfBuilder.Append(fsThis.ITAP.ToString(CultureInfo.InvariantCulture)); } // Margins _rtfBuilder.Append("\\li"); _rtfBuilder.Append(fsThis.LI.ToString(CultureInfo.InvariantCulture)); _rtfBuilder.Append("\\ri"); _rtfBuilder.Append(fsThis.RI.ToString(CultureInfo.InvariantCulture)); _rtfBuilder.Append("\\sa"); _rtfBuilder.Append(fsThis.SA.ToString(CultureInfo.InvariantCulture)); _rtfBuilder.Append("\\sb"); _rtfBuilder.Append(fsThis.SB.ToString(CultureInfo.InvariantCulture)); // Borders if (fsThis.HasParaBorder) { _rtfBuilder.Append(fsThis.ParaBorder.RTFEncoding); } // TextIndent if (dnThis.ListLabel != null) { _rtfBuilder.Append("\\jclisttab\\tx"); _rtfBuilder.Append(fsThis.LI.ToString(CultureInfo.InvariantCulture)); _rtfBuilder.Append("\\fi-360"); } else { _rtfBuilder.Append("\\fi"); _rtfBuilder.Append(fsThis.FI.ToString(CultureInfo.InvariantCulture)); } // Alignment switch (fsThis.HAlign) { case HAlign.AlignLeft: if (fsThis.DirPara != DirState.DirRTL) { _rtfBuilder.Append("\\ql"); } else { _rtfBuilder.Append("\\qr"); } break; case HAlign.AlignRight: if (fsThis.DirPara != DirState.DirRTL) { _rtfBuilder.Append("\\qr"); } else { _rtfBuilder.Append("\\ql"); } break; case HAlign.AlignCenter: _rtfBuilder.Append("\\qc"); break; case HAlign.AlignJustify: _rtfBuilder.Append("\\qj"); break; } // Background color if (fsThis.CBPara >= 0) { _rtfBuilder.Append("\\cbpat"); _rtfBuilder.Append(fsThis.CBPara.ToString(CultureInfo.InvariantCulture)); } // LineHeight if (fsThis.SL != 0) { _rtfBuilder.Append("\\sl"); _rtfBuilder.Append(fsThis.SL.ToString(CultureInfo.InvariantCulture)); _rtfBuilder.Append("\\slmult0"); } // omit \par if last paragraph in cell if (dnThis.IsLastParagraphInCell()) { DocumentNode dnCell = dnThis.GetParentOfType(DocumentNodeType.dnCell); dnCell.IsTerminated = true; if (fsThis.ITAP > 1) { _rtfBuilder.Append("\\nestcell"); _rtfBuilder.Append("{\\nonesttables\\par}"); } else { _rtfBuilder.Append("\\cell"); } _rtfBuilder.Append("\r\n"); } else { _rtfBuilder.Append("\\par"); } _rtfBuilder.Append("}"); _rtfBuilder.Append("\r\n"); }