private static void SerializeRow(SpreadsheetDocument sDoc, XmlWriter xw, int rowCount, RowDfn row, out int numColumns)
        {
            var ns = S.s.NamespaceName;

            xw.WriteStartElement("row", ns);
            xw.WriteStartAttribute("r");
            xw.WriteValue(rowCount);
            xw.WriteEndAttribute();
            xw.WriteStartAttribute("spans");
            xw.WriteValue("1:" + row.Cells.Count().ToString());
            xw.WriteEndAttribute();
            var cellCount = 0;

            foreach (var cell in row.Cells)
            {
                if (cell != null)
                {
                    xw.WriteStartElement("c", ns);
                    xw.WriteStartAttribute("r");
                    xw.WriteValue(SpreadsheetMLUtil.IntToColumnId(cellCount) + rowCount.ToString());
                    xw.WriteEndAttribute();
                    if (cell.Bold != null ||
                        cell.Italic != null ||
                        cell.FormatCode != null ||
                        cell.HorizontalCellAlignment != null)
                    {
                        xw.WriteStartAttribute("s");
                        xw.WriteValue(GetCellStyle(sDoc, cell));
                        xw.WriteEndAttribute();
                    }
                    switch (cell.CellDataType)
                    {
                    case CellDataType.Boolean:
                        xw.WriteStartAttribute("t");
                        xw.WriteValue("b");
                        xw.WriteEndAttribute();
                        break;

                    case CellDataType.Date:
                        xw.WriteStartAttribute("t");
                        xw.WriteValue("d");
                        xw.WriteEndAttribute();
                        break;

                    case CellDataType.Number:
                        xw.WriteStartAttribute("t");
                        xw.WriteValue("n");
                        xw.WriteEndAttribute();
                        break;

                    case CellDataType.String:
                        xw.WriteStartAttribute("t");
                        xw.WriteValue("str");
                        xw.WriteEndAttribute();
                        break;

                    default:
                        break;
                    }
                    if (cell.Value != null)
                    {
                        xw.WriteStartElement("v", ns);
                        xw.WriteValue(cell.Value);
                        xw.WriteEndElement();
                    }
                    xw.WriteEndElement();
                }
                cellCount++;
            }
            xw.WriteEndElement();
            numColumns = cellCount;
        }
        public static void AddWorksheet(SpreadsheetDocument sDoc, WorksheetDfn worksheetData)
        {
            var validSheetName = new Regex(@"^[^'*\[\]/\\:?][^*\[\]/\\:?]{0,30}$");

            if (!validSheetName.IsMatch(worksheetData.Name))
            {
                throw new InvalidSheetNameException(worksheetData.Name);
            }

            // throw WorksheetAlreadyExistsException if a sheet with the same name (case-insensitive) already exists in the workbook
            var UCName = worksheetData.Name.ToUpper();
            var wXDoc  = sDoc.WorkbookPart.GetXDocument();

            if (wXDoc
                .Root
                .Elements(S.sheets)
                .Elements(S.sheet)
                .Attributes(SSNoNamespace.name)
                .Select(a => ((string)a).ToUpper())
                .Contains(UCName))
            {
                throw new WorksheetAlreadyExistsException(worksheetData.Name);
            }

            // create the worksheet with the supplied name
            var appXDoc = sDoc
                          .ExtendedFilePropertiesPart
                          .GetXDocument();
            var vector = appXDoc
                         .Root
                         .Elements(EP.TitlesOfParts)
                         .Elements(VT.vector)
                         .FirstOrDefault();

            if (vector != null)
            {
                var size = (int?)vector.Attribute(SSNoNamespace.size);
                if (size == null)
                {
                    size = 1;
                }
                else
                {
                    size += 1;
                }

                vector.SetAttributeValue(SSNoNamespace.size, size);
                vector.Add(
                    new XElement(VT.lpstr, worksheetData.Name));
                var i4 = appXDoc
                         .Root
                         .Elements(EP.HeadingPairs)
                         .Elements(VT.vector)
                         .Elements(VT.variant)
                         .Elements(VT.i4)
                         .FirstOrDefault();
                if (i4 != null)
                {
                    i4.Value = ((int)i4 + 1).ToString();
                }

                sDoc.ExtendedFilePropertiesPart.PutXDocument();
            }

            var workbook      = sDoc.WorkbookPart;
            var rId           = "R" + Guid.NewGuid().ToString().Replace("-", "");
            var worksheetPart = workbook.AddNewPart <WorksheetPart>(rId);

            var wbXDoc = workbook.GetXDocument();
            var sheets = wbXDoc.Descendants(S.sheets).FirstOrDefault();

            sheets.Add(
                new XElement(S.sheet,
                             new XAttribute(SSNoNamespace.name, worksheetData.Name.ToString()),
                             new XAttribute(SSNoNamespace.sheetId, sheets.Elements(S.sheet).Count() + 1),
                             new XAttribute(R.id, rId)));
            workbook.PutXDocument();

            var ws    = S.s.ToString();
            var relns = R.r.ToString();

            using (var partStream = worksheetPart.GetStream(FileMode.Create, FileAccess.Write))
            {
                using var partXmlWriter = XmlWriter.Create(partStream);
                partXmlWriter.WriteStartDocument();
                partXmlWriter.WriteStartElement("worksheet", ws);
                partXmlWriter.WriteStartElement("sheetData", ws);

                var numColumnHeadingRows = 0;
                var numColumns           = 0;
                if (worksheetData.ColumnHeadings != null)
                {
                    var row = new RowDfn
                    {
                        Cells = worksheetData.ColumnHeadings
                    };
                    SerializeRows(sDoc, partXmlWriter, new[] { row }, 1, out numColumns, out numColumnHeadingRows);
                }
                SerializeRows(sDoc, partXmlWriter, worksheetData.Rows, numColumnHeadingRows + 1, out var numColumnsInRows,
                              out var numRows);
                var totalRows    = numColumnHeadingRows + numRows;
                var totalColumns = Math.Max(numColumns, numColumnsInRows);
                if (worksheetData.ColumnHeadings != null && worksheetData.TableName != null)
                {
                    partXmlWriter.WriteEndElement();
                    var rId2 = "R" + Guid.NewGuid().ToString().Replace("-", "");
                    partXmlWriter.WriteStartElement("tableParts", ws);
                    partXmlWriter.WriteStartAttribute("count");
                    partXmlWriter.WriteValue(1);
                    partXmlWriter.WriteEndAttribute();
                    partXmlWriter.WriteStartElement("tablePart", ws);
                    partXmlWriter.WriteStartAttribute("id", relns);
                    partXmlWriter.WriteValue(rId2);
                    var tdp   = worksheetPart.AddNewPart <TableDefinitionPart>(rId2);
                    var tXDoc = tdp.GetXDocument();
                    var table = new XElement(S.table,
                                             new XAttribute(SSNoNamespace.id, 1),
                                             new XAttribute(SSNoNamespace.name, worksheetData.TableName),
                                             new XAttribute(SSNoNamespace.displayName, worksheetData.TableName),
                                             new XAttribute(SSNoNamespace._ref, "A1:" + SpreadsheetMLUtil.IntToColumnId(totalColumns - 1) + totalRows.ToString()),
                                             new XAttribute(SSNoNamespace.totalsRowShown, 0),
                                             new XElement(S.autoFilter,
                                                          new XAttribute(SSNoNamespace._ref, "A1:" + SpreadsheetMLUtil.IntToColumnId(totalColumns - 1) + totalRows.ToString())),
                                             new XElement(S.tableColumns,
                                                          new XAttribute(SSNoNamespace.count, totalColumns),
                                                          worksheetData.ColumnHeadings.Select((ch, i) =>
                                                                                              new XElement(S.tableColumn,
                                                                                                           new XAttribute(SSNoNamespace.id, i + 1),
                                                                                                           new XAttribute(SSNoNamespace.name, ch.Value)))),
                                             new XElement(S.tableStyleInfo,
                                                          new XAttribute(SSNoNamespace.name, "TableStyleMedium2"),
                                                          new XAttribute(SSNoNamespace.showFirstColumn, 0),
                                                          new XAttribute(SSNoNamespace.showLastColumn, 0),
                                                          new XAttribute(SSNoNamespace.showRowStripes, 1),
                                                          new XAttribute(SSNoNamespace.showColumnStripes, 0)));
                    tXDoc.Add(table);
                    tdp.PutXDocument();
                }
            }
            sDoc.WorkbookPart.WorkbookStylesPart.PutXDocument();
            sDoc.WorkbookPart.WorkbookStylesPart.Stylesheet.Save();
        }