using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Utils; using Sarand.Net.Standard.Proc.Xml; using Sarand.Net.Standard.Proc.Zip; namespace Sarand.Net.Standard.Proc.Excel { //======================================================================= /// Style properties retrieved directly from spreadsheet xml data. public class SaMsExcelStyle { public SaMsExcelStyle () { } public int NumFmtId { get; set; } public int FontId { get; set; } public int FillId { get; set; } public int BorderId { get; set; } public int XfId { get; set; } public bool QuotePrefix { get; set; } public bool PivotButton { get; set; } public bool ApplyNumberFormat { get; set; } public bool ApplyFont { get; set; } public bool ApplyFill { get; set; } public bool ApplyBorder { get; set; } public bool ApplyAlignment { get; set; } public bool ApplyProtection { get; set; } public XlHAlign AlignHorizontal { get; set; } public XlVAlign AlignVertical { get; set; } public String NumFmtCode { get; set; } = ""; }; //======================================================================= /// Excel spreadsheet read directly from xml data rather than via COM/Interop. public class SaMsSpreadsheet_Direct : SaMsSpreadsheet { //------------------------------------------------------------------- // Data members. internal class Workbook { internal SaMsZipArchive WorkbookArchive { get; set; } internal SaMsXmlDocument WorkbookDoc { get; set; } internal SaMsXmlDocument SharedStringsDoc { get; set; } internal SaMsXmlDocument StylesDoc { get; set; } internal uint WorksheetCount { get; set; } internal uint ActiveSheetPos { get; set; } internal bool IsValid () { return (WorkbookDoc != null && WorkbookDoc.GetRootElement() != null); } } private Workbook workbook; private class Worksheet { internal uint WorksheetPos { get; set; } internal uint WorksheetId { get; set; } internal String WorksheetArchivePath { get; set; } internal String WorksheetName { get; set; } internal SaMsXmlDocument WorksheetDoc { get; set; } internal uint ActiveColPos { get; set; } internal uint ActiveRowPos { get; set; } internal class NodesData { internal SaMsXmlElement Cols { get; set; } internal SaMsXmlElement ActiveCol { get; set; } internal SaMsXmlElement Rows { get; set; } internal SaMsXmlElement ActiveRow { get; set; } } internal NodesData Nodes { get; } = new NodesData(); bool IsValid () { return (WorksheetDoc != null && WorksheetDoc.GetRootElement() != null); } } private Worksheet worksheet; //------------------------------------------------------------------- // Construction. public SaMsSpreadsheet_Direct () : base() { } protected override void Dispose (bool disposing) { if (disposing) { CloseWorkbook(); } base.Dispose(disposing); } //------------------------------------------------------------------- // Properties. //------------------------------------------------------------------- // W o r k b o o k //------------------------------------------------------------------- public override bool IsWorkbookValid () { return (workbook != null && workbook.IsValid()); } public override bool CreateWorkbook () { CloseWorkbook(); ClearErrorState(); // Create new workbook. workbook = new Workbook(); workbook.WorkbookArchive = new SaMsZipArchive(); workbook.SharedStringsDoc = new SaMsXmlDocument(); workbook.StylesDoc = new SaMsXmlDocument(); return SetErrorState("Creating workbook not supported"); } public override bool OpenWorkbook (String filePath) { CloseWorkbook(); ClearErrorState(); if (!File.Exists(filePath)) return SetErrorState("Workbook not found"); List validExtensions = new List() { /*".xls",*/ ".xlsx", ".xlsb", ".zip" }; String extension = Path.GetExtension(filePath); if (!validExtensions.ContainsCI(Path.GetExtension(filePath))) return SetErrorState("Invalid document type (expected xlsx/xlsb)"); try { // Open workbook document. workbook = new Workbook(); workbook.WorkbookArchive = new SaMsZipArchive(); if (workbook.WorkbookArchive.OpenArchive(filePath)) { ExtractWorkbookElementsFromArchive(); } else return SetErrorState("Failed to open document: " + workbook.WorkbookArchive.ErrorStateMessage); } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } return !HasErrorState(); } public bool OpenWorkbook (byte[] workbookData) { CloseWorkbook(); ClearErrorState(); try { using (MemoryStream workbookStream = new MemoryStream(workbookData)) { OpenWorkbook(workbookStream); } } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } return !HasErrorState(); } public bool OpenWorkbook (Stream workbookStream) { CloseWorkbook(); ClearErrorState(); try { // From start of stream. workbookStream.Seek(0, SeekOrigin.Begin); // Open workbook document. workbook = new Workbook(); workbook.WorkbookArchive = new SaMsZipArchive(); if (workbook.WorkbookArchive.OpenArchive(workbookStream)) { ExtractWorkbookElementsFromArchive(); } else return SetErrorState("Failed to open document: " + workbook.WorkbookArchive.ErrorStateMessage); } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } return !HasErrorState(); } private bool ExtractWorkbookElementsFromArchive () { ClearErrorState(); try { // Extract the workbook data. using (MemoryStream workbookFile = workbook.WorkbookArchive.ExtractFileToStream("xl/workbook.xml")) { if (workbookFile == null) return SetErrorState("Failed to load workbook data"); workbook.WorkbookDoc = new SaMsXmlDocument(); if (workbook.WorkbookDoc.LoadXml(workbookFile)) { // Workbook successfully opened. workbook.ActiveSheetPos = 1; // Shared strings: using (MemoryStream sharedStringsFile = workbook.WorkbookArchive.ExtractFileToStream("xl/sharedStrings.xml")) { if (sharedStringsFile != null) { workbook.SharedStringsDoc = new SaMsXmlDocument(); workbook.SharedStringsDoc.LoadXml(sharedStringsFile); } } // Styles: using (MemoryStream stylesFile = workbook.WorkbookArchive.ExtractFileToStream("xl/styles.xml")) { if (stylesFile != null) { workbook.StylesDoc = new SaMsXmlDocument(); workbook.StylesDoc.LoadXml(stylesFile); } } // Set worksheet count and open first worksheet. PopulateWorksheetCount(); OpenWorksheet(1); } else return SetErrorState("Failed to read workbook data"); } } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } return !HasErrorState(); } public override bool SaveDocument (String filePath, int fileFormat, bool bAutomaticallyOverwrite) { ClearErrorState(); if (fileFormat == 0) fileFormat = (int)XlFileFormat.xlOpenXMLWorkbook; if (fileFormat != (int)XlFileFormat.xlOpenXMLWorkbook) SetErrorState("Unsupported file format: " + ((XlFileFormat)fileFormat).ToString()); if (!HasErrorState() && !workbook.IsValid()) SetErrorState("Workbook is not open"); SetErrorState("Saving workbook not supported"); return !HasErrorState(); } public override bool CloseWorkbook () { ClearErrorState(); CloseWorksheet(); // Clear workbook. if (workbook != null) { try { if (workbook.StylesDoc != null) workbook.StylesDoc.Dispose(); workbook.StylesDoc = null; if (workbook.SharedStringsDoc != null) workbook.SharedStringsDoc.Dispose(); workbook.SharedStringsDoc = null; if (workbook.WorkbookDoc != null) workbook.WorkbookDoc.Dispose(); workbook.WorkbookDoc = null; if (workbook.WorkbookArchive != null) workbook.WorkbookArchive.Dispose(); workbook.WorkbookArchive = null; } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } workbook = null; } return !HasErrorState(); } //------------------------------------------------------------------- // W o r k s h e e t //------------------------------------------------------------------- public override bool IsWorksheetValid () { return (worksheet != null && worksheet.WorksheetDoc != null); } public override uint WorksheetCount { get { return workbook.WorksheetCount; } } private void PopulateWorksheetCount () { if (!IsWorkbookValid()) return; try { // Count worksheets in workbook. SaMsXmlElement nodeWorkbook = workbook.WorkbookDoc.GetRootElement(); SaMsXmlElement nodeSheets = nodeWorkbook.GetFirstChildElement("sheets"); workbook.WorksheetCount = (uint)nodeSheets.GetChildElements("sheet").Count; } catch (Exception exc) { HandleException("GetWorksheetCount", exc); } } public override bool SetWorksheetCount (int worksheetCount) { ClearErrorState(); return SetErrorState("Modifying workbook not supported"); } public override bool AddWorksheet(String name) { return SetErrorState("Modifying workbook not supported"); } public override bool RemoveWorksheet(uint worksheetPos) { return SetErrorState("Modifying workbook not supported"); } public override bool OpenWorksheet (uint worksheetPos) { ClearErrorState(); if (!workbook.IsValid()) return SetErrorState("Workbook is not open"); if (worksheetPos > WorksheetCount) return SetErrorState("Invalid sheet specified"); if (worksheetPos <= 0) worksheetPos = Math.Max(1, workbook.ActiveSheetPos); CloseWorksheet(); try { worksheet = new Worksheet(); // Locate worksheet in workbook by 0-index. SaMsXmlElement workbookElement = workbook.WorkbookDoc.GetRootElement(); SaMsXmlElement sheetsElement = workbookElement.GetFirstChildElement("sheets"); long sheetCount = 0; foreach (SaMsXmlElement sheetElement in sheetsElement.GetChildElements("sheet")) { sheetCount++; if (sheetCount == worksheetPos) { // Extract the worksheet data. uint sheetId = (uint)StringUtil.ToInt(sheetElement.GetAttributeValue("sheetId")); String worksheetArchivePath = "xl/worksheets/sheet" + worksheetPos.ToString() + ".xml"; using (MemoryStream worksheetFile = workbook.WorkbookArchive.ExtractFileToStream(worksheetArchivePath)) { if (worksheetFile == null) return SetErrorState("Failed to load worksheet data"); worksheet.WorksheetDoc = new SaMsXmlDocument(); if (worksheet.WorksheetDoc.LoadXml(worksheetFile)) { workbook.ActiveSheetPos = worksheetPos; worksheet.WorksheetPos = worksheetPos; worksheet.WorksheetId = sheetId; worksheet.WorksheetArchivePath = worksheetArchivePath; worksheet.WorksheetName = sheetElement.GetAttributeValue("name"); worksheet.ActiveColPos = 1; worksheet.ActiveRowPos = 1; SaMsXmlElement nodeWorksheet = worksheet.WorksheetDoc.GetRootElement(); worksheet.Nodes.Cols = nodeWorksheet.GetFirstChildElement("cols"); worksheet.Nodes.ActiveCol = worksheet.Nodes.Cols.GetFirstChildElement("col"); worksheet.Nodes.Rows = nodeWorksheet.GetFirstChildElement("sheetData"); worksheet.Nodes.ActiveRow = worksheet.Nodes.Rows.GetFirstChildElement("row"); workbook.ActiveSheetPos = worksheetPos; } else return SetErrorState("Failed to read data for worksheet " + worksheetPos.ToString()); } break; } } if (worksheet.WorksheetDoc == null) return SetErrorState("Unable to load worksheet " + worksheetPos.ToString()); } catch (Exception exc) { HandleException("OpenWorksheet", exc); } return !HasErrorState(); } public override uint WorksheetPos { get { return workbook.ActiveSheetPos; } } public override String WorksheetName { get { return ""; } set { } } public override bool SetWorksheetAttr (SaMsExcelItemAttr attr) { return SetErrorState("Not supported"); } public override bool CloseWorksheet () { ClearErrorState(); if (worksheet != null) { try { if (worksheet.WorksheetDoc != null) worksheet.WorksheetDoc.Dispose(); worksheet.WorksheetDoc = null; if (worksheet.Nodes.Cols != null) worksheet.Nodes.Cols.Dispose(); worksheet.Nodes.Cols = null; if (worksheet.Nodes.ActiveCol != null) worksheet.Nodes.ActiveCol.Dispose(); worksheet.Nodes.ActiveCol = null; if (worksheet.Nodes.Rows != null) worksheet.Nodes.Rows.Dispose(); worksheet.Nodes.Rows = null; if (worksheet.Nodes.ActiveRow != null) worksheet.Nodes.ActiveRow.Dispose(); worksheet.Nodes.ActiveRow = null; } catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); } worksheet = null; } return !HasErrorState(); } //------------------------------------------------------------------- // Worksheet Page Setup. public override bool SetPageSetup (SaMsExcelPageSetup_Config pageSetupConfig) { return SetErrorState("Not supported"); } public override bool SetPageBreak (uint col, uint row) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- // C o l u m n s //------------------------------------------------------------------- public override uint GetActiveColPos () { return worksheet.ActiveColPos; } private SaMsXmlElement GetColElement (uint colPos) { ClearErrorState(); try { if (worksheet.ActiveColPos == colPos) return worksheet.Nodes.ActiveCol; int colCount = 0; foreach (SaMsXmlElement colElement in worksheet.Nodes.Cols.GetChildElements("Col")) { if (++colCount == colPos) { if (worksheet.Nodes.ActiveCol != null) worksheet.Nodes.ActiveCol.Dispose(); worksheet.ActiveColPos = colPos; worksheet.Nodes.ActiveCol = colElement; return colElement; } } } catch (Exception exc) { HandleException("GetColElement", exc); } return new SaMsXmlElement(null); } public override double GetColWidth (uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point) { ClearErrorState(); try { // Retrieve width (in points) from column. SaMsXmlNode nodeCol = GetColElement(colPos); double width = nodeCol.GetAttributeValue("width").ToDouble(); // Adjust for units. if (unit == SaExcelLogicalUnit.Pixel) return PointsToPixels(width, false); if (unit == SaExcelLogicalUnit.Cm) return PointsToCms(width); if (unit == SaExcelLogicalUnit.Inch) return PointsToInches(width); return width; } catch (Exception exc) { HandleException("GetColWidth", exc); } return 0.0; } public override bool SetColWidth (uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- // R o w s //------------------------------------------------------------------- public override uint GetActiveRowPos () { return worksheet.ActiveRowPos; } SaMsXmlElement GetRowElement (uint rowPos) { ClearErrorState(); try { if (worksheet.ActiveRowPos == rowPos) return worksheet.Nodes.ActiveRow; foreach (SaMsXmlElement rowElement in worksheet.Nodes.Rows.GetChildElements("row")) { if (rowElement.GetAttributeValue("r").ToLong() == rowPos) { if (worksheet.Nodes.ActiveRow != null) worksheet.Nodes.ActiveRow.Dispose(); worksheet.ActiveRowPos = rowPos; worksheet.Nodes.ActiveRow = rowElement; return rowElement; } } } catch (Exception exc) { HandleException("GetRowElement", exc); } return new SaMsXmlElement(null); } public override double GetRowHeight (uint rowPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point) { ClearErrorState(); try { // Retrieve height (in points) from row. SaMsXmlElement rowElement = GetRowElement(rowPos); double height = rowElement.GetAttributeValue("ht").ToDouble(); // Adjust for units. if (unit == SaExcelLogicalUnit.Pixel) return PointsToPixels(height, true); if (unit == SaExcelLogicalUnit.Cm) return PointsToCms(height); if (unit == SaExcelLogicalUnit.Inch) return PointsToInches(height); return height; } catch (Exception exc) { HandleException("GetRowHeight", exc); } return 0.0; } public override bool SetRowHeight (uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- // R o w s //------------------------------------------------------------------- public override List GetRowDataValues (uint rowPos, int maxColumns = 0) { ClearErrorState(); List values = new List(); try { SaMsXmlElement rowElement = GetRowElement(rowPos); foreach (SaMsXmlElement cellElement in rowElement.GetChildElements("c")) { // CellId may be needed in case cells are skipped in the data. // Doesn't appear to be the case, even when previous cell is merged with next. String cellId = cellElement.GetAttributeValue("r"); values.Add(GetCellDataValue(cellElement)); if (maxColumns > 0 && values.Count >= maxColumns) break; } } catch (Exception exc) { HandleException("GetRowDataValues", exc); } return values; } //------------------------------------------------------------------- // C e l l s //------------------------------------------------------------------- SaMsXmlElement GetCellElement (uint colPos, uint rowPos) { ClearErrorState(); String cellId = GetCellId(colPos,rowPos); try { SaMsXmlElement rowElement = GetRowElement(rowPos); foreach (SaMsXmlElement cellElement in rowElement.GetChildElements("c")) { if (cellElement.GetAttributeValue("r").EqualsCI(cellId)) return cellElement; } } catch (Exception exc) { HandleException("GetCellElement", exc); } return new SaMsXmlElement(null); } public override void SetActiveCell (uint colPos, uint rowPos) { // ToDo SetActiveCell. SetErrorState("Not supported"); } //------------------------------------------------------------------- private bool IsCellFormula (uint colPos, uint rowPos) { ClearErrorState(); bool bIsFormula = false; try { SaMsXmlElement cellElement = GetCellElement(colPos,rowPos); bIsFormula = !cellElement.GetAttributeValue("f").IsEmpty(); } catch (Exception exc) { HandleException("IsCellFormula", exc); } return bIsFormula; } private bool IsCellNumeric (uint colPos, uint rowPos) { ClearErrorState(); try { SaMsXmlElement cellElement = GetCellElement(colPos,rowPos); SaMsExcelStyle cellStyle = GetCellStyle(cellElement); return IsCellNumeric(cellElement, cellStyle); } catch (Exception exc) { HandleException("IsCellNumeric", exc); } return false; } private bool IsCellNumeric (SaMsXmlElement cellElement) { ClearErrorState(); SaMsExcelStyle cellStyle = GetCellStyle(cellElement); return IsCellNumeric(cellElement, cellStyle); } private bool IsCellNumeric (SaMsXmlElement cellElement, SaMsExcelStyle cellStyle) { ClearErrorState(); return (cellElement.GetAttributeValue("t").IsEmpty() && !IsDateTimeStyle(cellStyle.NumFmtCode)); } private bool IsCellDate (uint colPos, uint rowPos) { ClearErrorState(); bool bIsDate = false; try { SaMsXmlElement cellElement = GetCellElement(colPos,rowPos); SaMsExcelStyle cellStyle = GetCellStyle(cellElement); return IsCellDate(cellElement, cellStyle); } catch (Exception exc) { HandleException("IsCellDate", exc); } return bIsDate; } private bool IsCellDate (SaMsXmlElement cellElement) { return IsCellDate(cellElement, GetCellStyle(cellElement)); } private bool IsCellDate (SaMsXmlElement cellElement, SaMsExcelStyle cellStyle) { ClearErrorState(); return IsDateTimeStyle(cellStyle.NumFmtCode); } //------------------------------------------------------------------- public override String GetCellFormula (uint colPos, uint rowPos) { ClearErrorState(); try { SaMsXmlElement cellElement = GetCellElement(colPos,rowPos); String formula = cellElement.GetAttributeValue("f"); if (formula.IsEmpty()) formula = GetCellText(cellElement, true); return formula; } catch (Exception exc) { HandleException("GetCellFormula", exc); } return ""; } private String GetCellText (SaMsXmlElement cellElement, bool trimValue = true) { ClearErrorState(); String value = ""; try { if (cellElement.GetAttributeValue("t").EqualsCI("s")) value = GetSharedString(cellElement); else value = cellElement.GetChildText("v"); if (trimValue) value.TrimAll(); } catch (Exception exc) { HandleException("GetCellText", exc); } return value; } public override DataValue GetCellDataValue (uint colPos, uint rowPos) { return GetCellDataValue(GetCellElement(colPos, rowPos), false); } private DataValue GetCellDataValue (SaMsXmlElement cellElement, bool trimTextValue = true) { ClearErrorState(); try { SaMsExcelStyle cellStyle = GetCellStyle(cellElement); if (cellElement.GetAttributeValue("t").EqualsCI("s")) { String value = GetSharedString(cellElement); if (trimTextValue) value = value.TrimAll(); return new DataValue(value); } else { String value = cellElement.GetChildText("v"); if (IsDateTimeStyle(cellStyle.NumFmtCode)) return new DataValue(DateTime.FromOADate(value.ToDouble())); if (IsCellNumeric(cellElement, cellStyle)) { if (value.IsNumeric()) return new DataValue(value.ToLong()); if (value.IsDecimal()) { #if DEBUG //if (StringUtil.ToInt(value) > 600) Debugger.Break(); #endif return new DataValue(value.ToDecimal()); } } if (trimTextValue) value = value.TrimAll(); return new DataValue(value); } } catch (Exception exc) { HandleException("GetCellDataValue", exc); } return new DataValue(); } //------------------------------------------------------------------- // Shared string for cell. private String GetSharedString (SaMsXmlElement nodeElement) { ClearErrorState(); try { SaMsXmlElement sharedStringElement = GetSharedStringElement(nodeElement); return sharedStringElement.GetChildText("t"); } catch (Exception exc) { HandleException("GetSharedString", exc); } return ""; } private SaMsXmlElement GetSharedStringElement (SaMsXmlElement cellElement) { ClearErrorState(); if (workbook.SharedStringsDoc == null || cellElement == null || !cellElement.Valid) return null; if (!cellElement.GetAttributeValue("t").EqualsCI("s") || !cellElement.GetChildText("v").IsNumeric()) return null; // Shared strings are indexed from 0. long sharedStringIdx = -1; try { long iIdx = cellElement.GetChildText("v").ToLong(); SaMsXmlElement sstElement = workbook.SharedStringsDoc.GetRootElement(); foreach (SaMsXmlElement siElement in sstElement.GetChildElements("si")) { if (++sharedStringIdx == iIdx) return siElement; } } catch (Exception exc) { HandleException("GetSharedStringNode", exc); } return new SaMsXmlElement(null); } //------------------------------------------------------------------- public override bool SetCellFormula (uint colPos, uint rowPos, String value) { return SetErrorState("Not supported"); } public override bool SetCellFormula (uint colPos, uint rowPos, String value, SaMsExcelItemAttr attr) { return SetErrorState("Not supported"); } public override bool SetRowFormulas (uint colPosStart, uint rowPos, List values) { return SetErrorState("Not supported"); } public override bool SetRangeFormulas (SaMsExcelRange range, List values) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- // C e l l S t y l e //------------------------------------------------------------------- private String GetBuiltInStyleFormat (int numFmtId) { // Ref. http://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value switch (numFmtId) { // case 0: return "General"; case 1: return "0"; case 2: return "0.00"; case 3: return "#,##0"; case 4: return "#,##0.00"; // case 9: return "0%"; case 10: return "0.00%"; case 11: return "0.00E+00"; case 12: return "# ?/?"; case 13: return "# ??/??"; case 14: return "mm-dd-yy"; case 15: return "d-mmm-yy"; case 16: return "d-mmm"; case 17: return "mmm-yy"; case 18: return "h:mm AM/PM"; case 19: return "h:mm:ss AM/PM"; case 20: return "h:mm"; case 21: return "h:mm:ss"; case 22: return "m/d/yy h:mm"; // case 37: return "#,##0 ;(#,##0)"; case 38: return "#,##0 ;[Red](#,##0)"; case 39: return "#,##0.00;(#,##0.00)"; case 40: return "#,##0.00;[Red](#,##0.00)"; // case 44: return "_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)"; case 45: return "mm:ss"; case 46: return "[h]:mm:ss"; case 47: return "mmss.0"; case 48: return "##0.0E+0"; case 49: return "@"; // case 27: return "[$-404]e/m/d"; case 30: return "m/d/yy"; case 36: return "[$-404]e/m/d"; case 50: return "[$-404]e/m/d"; case 57: return "[$-404]e/m/d"; // case 59: return "t0"; case 60: return "t0.00"; case 61: return "t#,##0"; case 62: return "t#,##0.00"; case 67: return "t0%"; case 68: return "t0.00%"; case 69: return "t# ?/?"; case 70: return "t# ??/??"; } return ""; } private SaMsExcelStyle GetCellStyle (SaMsXmlElement cellElement) { ClearErrorState(); SaMsExcelStyle style = new SaMsExcelStyle(); if (workbook.SharedStringsDoc == null || cellElement == null || !cellElement.Valid || !cellElement.GetAttributeValue("s").IsNumeric()) return style; // Styles are indexed from 0. long styleIdx = -1; try { long cellStyleIdx = cellElement.GetAttributeValue("s").ToLong(); SaMsXmlElement styleSheetElement = workbook.SharedStringsDoc.GetRootElement(); SaMsXmlElement cellXfsElement = styleSheetElement.GetFirstChildElement("cellXfs"); foreach (SaMsXmlElement xfElement in styleSheetElement.GetChildElements("xf")) { if (++styleIdx == cellStyleIdx) { // Construct style. style.NumFmtId = (int)xfElement.GetAttributeValue("numFmtId").ToLong(); style.FontId = (int)xfElement.GetAttributeValue("fontId").ToLong(); style.FillId = (int)xfElement.GetAttributeValue("fillId").ToLong(); style.BorderId = (int)xfElement.GetAttributeValue("borderId").ToLong(); style.XfId = (int)xfElement.GetAttributeValue("xfId").ToLong(); style.QuotePrefix = xfElement.GetAttributeValue("quotePrefix").ToBool(); style.PivotButton = xfElement.GetAttributeValue("pivotButton").ToBool(); style.ApplyNumberFormat = xfElement.GetAttributeValue("applyNumberFormat").ToBool(); style.ApplyFont = xfElement.GetAttributeValue("applyFont").ToBool(); style.ApplyFill = xfElement.GetAttributeValue("applyFill").ToBool(); style.ApplyBorder = xfElement.GetAttributeValue("applyBorder").ToBool(); style.ApplyAlignment = xfElement.GetAttributeValue("applyAlignment").ToBool(); style.ApplyProtection = xfElement.GetAttributeValue("applyProtection").ToBool(); // Alignment. SaMsXmlNode nodeAlignment = xfElement.GetFirstChildElement("alignment"); String sAlignHorizontal = nodeAlignment.GetAttributeValue("horizontal"); if (sAlignHorizontal.EqualsCI("center")) style.AlignHorizontal = XlHAlign.xlHAlignCenter; else if (sAlignHorizontal.EqualsCI("centerContinuous")) style.AlignHorizontal = XlHAlign.xlHAlignCenterAcrossSelection; else if (sAlignHorizontal.EqualsCI("distributed")) style.AlignHorizontal = XlHAlign.xlHAlignDistributed; else if (sAlignHorizontal.EqualsCI("fill")) style.AlignHorizontal = XlHAlign.xlHAlignFill; else if (sAlignHorizontal.EqualsCI("justify")) style.AlignHorizontal = XlHAlign.xlHAlignJustify; else if (sAlignHorizontal.EqualsCI("left")) style.AlignHorizontal = XlHAlign.xlHAlignLeft; else if (sAlignHorizontal.EqualsCI("right")) style.AlignHorizontal = XlHAlign.xlHAlignRight; String sAlignVertical = nodeAlignment.GetAttributeValue("vertical"); if (sAlignVertical.EqualsCI("center")) style.AlignVertical = XlVAlign.xlVAlignCenter; else if (sAlignVertical.EqualsCI("top")) style.AlignVertical = XlVAlign.xlVAlignTop; else if (sAlignVertical.EqualsCI("bottom")) style.AlignVertical = XlVAlign.xlVAlignBottom; else if (sAlignVertical.EqualsCI("distributed")) style.AlignVertical = XlVAlign.xlVAlignDistributed; else if (sAlignVertical.EqualsCI("justify")) style.AlignVertical = XlVAlign.xlVAlignJustify; // Number format. if (style.ApplyNumberFormat) { if (style.NumFmtId >= 164) { SaMsXmlElement numFmtsElement = styleSheetElement.GetFirstChildElement("NumFmts"); foreach (SaMsXmlElement numFmtElement in numFmtsElement.GetChildElements("numFmt")) { if (numFmtElement.GetAttributeValue("numFmtId").ToLong() == style.NumFmtId) { style.NumFmtCode = numFmtElement.GetAttributeValue("formatCode"); break; } } } else style.NumFmtCode = GetBuiltInStyleFormat(style.NumFmtId); } // Font. if (style.ApplyFont) { } // Fill. if (style.ApplyFill) { } // Borders. if (style.ApplyBorder) { } // Alignment. if (style.ApplyAlignment) { } // Protection. if (style.ApplyProtection) { } break; } } } catch (Exception exc) { HandleException("GetStyleNode", exc); } return style; } private bool IsDateStyle (String numFormat) { // Ref. http://stackoverflow.com/questions/19034805/how-to-distinguish-inline-numbers-from-ole-automation-date-numbers-in-openxml-sp/19582685#19582685 if (IsTimeStyle(numFormat)) return false; if (numFormat.Contains("d")) return true; if (numFormat.Contains("m")) return true; if (numFormat.Contains("y")) return true; return false; } private bool IsTimeStyle (String numFormat) { if (numFormat.Contains("h")) return true; if (numFormat.Contains(":mm")) return true; if (numFormat.Contains("mm:")) return true; if (numFormat.Contains(":ss")) return true; return false; } private bool IsDateTimeStyle (String numFormat) { return (IsDateStyle(numFormat) || IsTimeStyle(numFormat)); } //------------------------------------------------------------------- // Cell attributes. public override bool SetRangeAttr (SaMsExcelRange range, SaMsExcelItemAttr attr) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- // Add image to sheet. public override bool InsertImage (SaMsExcelImage_Config imageConfig) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- public override bool FreezePanes (uint colPos, uint rowPos) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- public override bool AutoFit (SaMsExcelRange range) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- public override bool SelectRange (SaMsExcelRange range) { return SetErrorState("Not supported"); } //------------------------------------------------------------------- } //======================================================================= }