using System; using System.IO; using System.Linq; using System.Xml; using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; namespace Neo.Afx.Services.Documents { public partial class DocumentUtilities { #region GenerateXlsxFromXml /// /// Generates the XLSX from XML. /// /// The memory stream to write to. /// The data in XML format. public void GenerateXlsxFromXml(MemoryStream ms, XmlDocument data) { var spreadSheet = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook); var workbookPart = spreadSheet.AddWorkbookPart(); workbookPart.Workbook = new Workbook(); spreadSheet.WorkbookPart.Workbook.AppendChild(new FileVersion { ApplicationName = "Microsoft Office Excel" }); var worksheetPart = workbookPart.AddNewPart(); worksheetPart.Worksheet = new Worksheet(new SheetData()); var workbookStylesPart = workbookPart.AddNewPart(); workbookStylesPart.Stylesheet = CreateStylesheet(); workbookStylesPart.Stylesheet.Save(); var sheets = spreadSheet.WorkbookPart.Workbook.AppendChild(new Sheets()); var sheet = new Sheet { Id = spreadSheet.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Exported Data" }; sheets.Append(sheet); #region SharedStringTablePart // Get the SharedStringTablePart. If it does not exist, create a new one. var shareStringPart = spreadSheet.WorkbookPart.GetPartsOfType().Count() > 0 ? spreadSheet.WorkbookPart.GetPartsOfType().First() : spreadSheet.WorkbookPart.AddNewPart(); #endregion #region Insert Data var rowIndex = (uint)1; var cellIndex = "A"; var cellCount = 1; //keep count so we can correctly filter heading and not have to many columns //do header record foreach(XmlNode childNode in data.FirstChild.ChildNodes[0]) { InsertCellInWorksheet(cellIndex, rowIndex, childNode.InnerText, 1U, worksheetPart, shareStringPart); if(cellCount < data.FirstChild.ChildNodes[0].ChildNodes.Count) { cellIndex = IncrementColRef(cellIndex); cellCount++; } } //filter first row var autoFilter = new AutoFilter { Reference = "A1:" + cellIndex + rowIndex }; worksheetPart.Worksheet.Append(autoFilter); //do row records foreach(XmlNode childNode in data.FirstChild.ChildNodes[1]) { rowIndex++; cellIndex = "A"; var styleIndex = rowIndex % 2 == 0 ? 2U : 3U; foreach(XmlNode cells in childNode.ChildNodes) { InsertCellInWorksheet(cellIndex, rowIndex, cells.InnerText, styleIndex, worksheetPart, shareStringPart); cellIndex = IncrementColRef(cellIndex); } } #endregion spreadSheet.WorkbookPart.Workbook.Save(); spreadSheet.Close(); } #endregion #region IncrementColRef internal string IncrementColRef(string lastRef) { var characters = lastRef.ToUpperInvariant().ToCharArray(); var sum = 0; foreach(var t in characters) { sum *= 26; sum += (t - 'A' + 1); } sum++; var columnName = String.Empty; while(sum > 0) { var modulo = (sum - 1) % 26; columnName = Convert.ToChar(65 + modulo) + columnName; sum = ((sum - modulo) / 26); } return columnName; } #endregion #region InsertCellInWorksheet internal void InsertCellInWorksheet(string columnName, uint rowIndex, string cellValue, uint styleIndex, WorksheetPart worksheetPart, SharedStringTablePart shareStringPart) { var worksheet = worksheetPart.Worksheet; var sheetData = worksheet.GetFirstChild(); var cellReference = columnName + rowIndex; var row = new Row(); if(sheetData.Elements().Where(r => r.RowIndex.Value == rowIndex).Count() != 0) { row = sheetData.Elements().Where(r => r.RowIndex.Value == rowIndex).First(); } else { row.RowIndex = Convert.ToUInt32(rowIndex); sheetData.Append(row); } var cell = new Cell { StyleIndex = styleIndex }; if(row.Elements().Where(c => c.CellReference.Value == cellReference).Count() > 0) { cell = row.Elements().Where(c => c.CellReference.Value == cellReference).First(); } else { cell.CellReference = cellReference; row.Append(cell); } var index = InsertSharedStringItem(cellValue, shareStringPart); cell.CellValue = new CellValue(index.ToString()); cell.DataType = new EnumValue(CellValues.SharedString); worksheet.Save(); } #endregion #region InsertSharedStringItem // Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text // and inserts it into the SharedStringTablePart. If the item already exists, returns its index. internal int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart) { // If the part does not contain a SharedStringTable, create one. if(shareStringPart.SharedStringTable == null) { shareStringPart.SharedStringTable = new SharedStringTable(); } int i = 0; // Iterate through all the items in the SharedStringTable. If the text already exists, return its index. foreach(SharedStringItem item in shareStringPart.SharedStringTable.Elements()) { if(item.InnerText == text) { return i; } i++; } // The text does not exist in the part. Create the SharedStringItem and return its index. shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text))); shareStringPart.SharedStringTable.Save(); return i; } #endregion #region CreateStylesheet internal Stylesheet CreateStylesheet() { var stylesheet = new Stylesheet { MCAttributes = new MarkupCompatibilityAttributes { Ignorable = "x14ac" } }; stylesheet.AddNamespaceDeclaration("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); stylesheet.AddNamespaceDeclaration("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); #region Fonts var fonts = new Fonts { Count = 2U, KnownFonts = true }; var headerFont = new Font { FontSize = new FontSize { Val = 9D }, Bold = new Bold(), Color = new Color { Rgb = _database.GetSysSetting("ExcelExportHeaderFG") }, FontName = new FontName { Val = "Calibri" }, FontFamilyNumbering = new FontFamilyNumbering { Val = 2 }, FontScheme = new FontScheme { Val = FontSchemeValues.Minor } }; var mainFont = new Font { FontSize = new FontSize { Val = 9D }, Color = new Color { Rgb = _database.GetSysSetting("ExcelExportCellFG") }, FontName = new FontName { Val = "Calibri" }, FontFamilyNumbering = new FontFamilyNumbering { Val = 2 }, FontScheme = new FontScheme { Val = FontSchemeValues.Minor } }; fonts.Append(headerFont); fonts.Append(mainFont); #endregion #region Fills var fills = new Fills { Count = 5U }; #region generalFill = 0, none var generalFill = new Fill(); var generalFillPattern = new PatternFill { PatternType = PatternValues.None }; generalFill.Append(generalFillPattern); #endregion #region grayFill = 1, none var grayFill = new Fill(); var grayFillPattern = new PatternFill { PatternType = PatternValues.Gray125 }; grayFill.Append(grayFillPattern); #endregion #region headerFill = 2, Blue Header var headerFill = new Fill(); var headerFillPattern = new PatternFill { PatternType = PatternValues.Solid }; var headerForegroundColor = new ForegroundColor { Rgb = _database.GetSysSetting("ExcelExportHeaderFG") }; var headerBackgroundColor = new BackgroundColor { Indexed = 64U }; headerFillPattern.Append(headerForegroundColor); headerFillPattern.Append(headerBackgroundColor); headerFill.Append(headerFillPattern); #endregion #region rowFill = 3, Light blue row var rowFill = new Fill(); var rowFillPattern = new PatternFill { PatternType = PatternValues.Solid }; var rowForegroundColor = new ForegroundColor { Rgb = _database.GetSysSetting("ExcelExportRowBG") }; var rowBackgroundColor = new BackgroundColor { Indexed = 64U }; rowFillPattern.Append(rowForegroundColor); rowFillPattern.Append(rowBackgroundColor); rowFill.Append(rowFillPattern); #endregion #region alternateFill = 4, white alternate var alternateFill = new Fill(); var alternateFillPattern = new PatternFill { PatternType = PatternValues.Solid }; var alternateForegroundColor = new ForegroundColor { Rgb = _database.GetSysSetting("ExcelExportRowAlt") }; var alternateBackgroundColor = new BackgroundColor { Indexed = 64U }; alternateFillPattern.Append(alternateForegroundColor); alternateFillPattern.Append(alternateBackgroundColor); alternateFill.Append(alternateFillPattern); #endregion fills.Append(generalFill); fills.Append(grayFill); fills.Append(headerFill); fills.Append(rowFill); fills.Append(alternateFill); #endregion #region Borders var borders = new Borders { Count = 2U }; var noBorder = new Border { LeftBorder = new LeftBorder(), RightBorder = new RightBorder(), TopBorder = new TopBorder(), BottomBorder = new BottomBorder(), DiagonalBorder = new DiagonalBorder() }; var bottomOnlyBorder = new Border { LeftBorder = new LeftBorder(), RightBorder = new RightBorder(), TopBorder = new TopBorder(), BottomBorder = new BottomBorder { Style = BorderStyleValues.Thin, Color = new Color { Rgb = _database.GetSysSetting("ExcelExportRowBottomBorder") } }, DiagonalBorder = new DiagonalBorder() }; borders.Append(noBorder); borders.Append(bottomOnlyBorder); #endregion #region Cell Formatting #region CellStyleFormats var cellStyleFormats = new CellStyleFormats { Count = 1U }; var cellFormat = new CellFormat { NumberFormatId = 0U, FontId = 1U, FillId = 0U, BorderId = 0U }; cellStyleFormats.Append(cellFormat); #endregion #region CellFormats var cellFormats = new CellFormats { Count = 4U }; var generalFormat = new CellFormat { NumberFormatId = 0U, FontId = 1U, FillId = 0U, BorderId = 0U, FormatId = 0U }; var headerFormat = new CellFormat { NumberFormatId = 0U, FontId = 0U, FillId = 2U, BorderId = 0U, FormatId = 0U, ApplyFill = true }; var rowFormat = new CellFormat { NumberFormatId = 0U, FontId = 1U, FillId = 3U, BorderId = 1U, FormatId = 0U, ApplyFill = true }; var alternateFormat = new CellFormat { NumberFormatId = 0U, FontId = 1U, FillId = 4U, BorderId = 1U, FormatId = 0U, ApplyFill = true }; cellFormats.Append(generalFormat); cellFormats.Append(headerFormat); cellFormats.Append(rowFormat); cellFormats.Append(alternateFormat); #endregion #region CellStyles var cellStyles = new CellStyles { Count = 1U }; var cellStyle = new CellStyle { Name = "Normal", FormatId = 0U, BuiltinId = 0U }; cellStyles.Append(cellStyle); #endregion #endregion stylesheet.Append(fonts); stylesheet.Append(fills); stylesheet.Append(borders); stylesheet.Append(cellStyleFormats); stylesheet.Append(cellFormats); stylesheet.Append(cellStyles); return stylesheet; } #endregion } }