Files
Common-VS2019/SacsStandardProc/Proc/Excel/SaMsSpreadsheet_Direct.cs
T
2025-02-08 16:41:25 +00:00

1023 lines
36 KiB
C#

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
{
//=======================================================================
/// <summary>Style properties retrieved directly from spreadsheet xml data.</summary>
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; } = "";
};
//=======================================================================
/// <summary>Excel spreadsheet read directly from xml data rather than via COM/Interop.</summary>
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<String> validExtensions = new List<string>() { /*".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 OK;
}
public bool OpenWorkbook(byte[] workbookData)
{
CloseWorkbook();
ClearErrorState();
try
{
using (MemoryStream workbookStream = new MemoryStream(workbookData))
{
OpenWorkbook(workbookStream);
}
}
catch (Exception exc) { SetErrorState(ExceptionUtil.GetErrorStateMessage(exc)); }
return OK;
}
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 OK;
}
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 OK;
}
public override bool CleanseWorkbook()
{
throw new NotSupportedException(SysUtil.Fn);
}
public override bool SaveDocument(String filePath, int fileFormat, bool bAutomaticallyOverwrite)
{
ClearErrorState();
if (fileFormat == 0)
fileFormat = (int)XlFileFormat.xlOpenXMLWorkbook;
if (fileFormat != (int)XlFileFormat.xlOpenXMLWorkbook)
return SetErrorState("Unsupported file format: " + ((XlFileFormat)fileFormat).ToString());
if (!IsWorkbookValid()) SetErrorState(Msg_WorkbookNotOpen);
SetErrorState("Saving workbook not supported");
return OK;
}
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 OK;
}
/// <summary>Extract the document as a byte array.</summary>
/// <returns>Document data as byte array.</returns>
public override byte[] GetDocumentData()
{
throw new NotSupportedException(SysUtil.Fn);
}
// Default style.
public override void ApplyDefaultStyle(SaMsExcelStyleAttr attr)
{
throw new NotSupportedException(SysUtil.Fn);
}
//-------------------------------------------------------------------
// W o r k s h e e t s
//-------------------------------------------------------------------
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(SysUtil.Fn, exc); }
}
public override bool SetWorksheetCount(int worksheetCount)
{
ClearErrorState();
return SetErrorState("Modifying workbook not supported");
}
//-------------------------------------------------------------------
public override List<String> WorksheetNames
{
get
{
List<String> names = new List<String>();
return names;
}
}
//-------------------------------------------------------------------
/// <summary>
/// Open worksheet by position (from 1).
/// </summary>
public override bool OpenWorksheet(uint worksheetPos)
{
ClearErrorState();
if (!IsWorkbookValid()) return SetErrorState(Msg_WorkbookNotOpen);
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(SysUtil.Fn, exc); }
return OK;
}
//-------------------------------------------------------------------
/// <summary>
/// Open worksheet by position (from 1).
/// </summary>
public override bool OpenWorksheet(String name)
{
ClearErrorState();
if (!IsWorkbookValid()) return SetErrorState(Msg_WorkbookNotOpen);
CloseWorksheet();
name = StringUtil.TrimWhitespace(name);
try
{
worksheet = new Worksheet();
// Locate worksheet in workbook by 0-index.
SaMsXmlElement workbookElement = workbook.WorkbookDoc.GetRootElement();
SaMsXmlElement sheetsElement = workbookElement.GetFirstChildElement("sheets");
uint worksheetPos = 0;
foreach (SaMsXmlElement sheetElement in sheetsElement.GetChildElements("sheet"))
{
worksheetPos++;
String sheetName = sheetElement.GetAttributeValue("name");
if (StringUtil.TrimWhitespace(sheetName).EqualsCI(name))
{
// 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 = sheetName;
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(SysUtil.Fn, exc); }
return OK;
}
//-------------------------------------------------------------------
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 OK;
}
//-------------------------------------------------------------------
public override bool AddWorksheet(String name)
{
return SetErrorState("Modifying workbook not supported");
}
public override bool RemoveWorksheet(uint worksheetPos)
{
return SetErrorState("Modifying workbook not supported");
}
//-------------------------------------------------------------------
// W o r k s h e e t
//-------------------------------------------------------------------
public override bool IsWorksheetValid()
{
return (worksheet != null && worksheet.WorksheetDoc != null);
}
//-------------------------------------------------------------------
public override uint WorksheetPos { get { return workbook.ActiveSheetPos; } }
public override String WorksheetName
{
get { return ""; }
set { }
}
public override bool SetWorksheetAttr(SaMsExcelStyleAttr attr)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// Worksheet Page Setup.
public override bool SetPageSetup(SaMsExcelPageSetup_Config pageSetupConfig)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
public override bool SetPageBreak(uint col, uint row)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// C o l u m n s
//-------------------------------------------------------------------
public override uint ActiveColPos { get { 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(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return 0.0;
}
public override bool SetColWidth(uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public override uint FirstRowPos
{
get
{
SaMsXmlElement rowElement = worksheet.Nodes.Rows.GetChildElements("row").First();
return (uint)(rowElement?.GetAttributeValue("r").ToLong() ?? 0);
}
}
public override uint LastRowPos
{
get
{
SaMsXmlElement rowElement = worksheet.Nodes.Rows.GetChildElements("row").Last();
return (uint)(rowElement?.GetAttributeValue("r").ToLong() ?? 0);
}
}
public override uint ActiveRowPos { get { 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(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return 0.0;
}
public override bool SetRowHeight(uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// R o w s
//-------------------------------------------------------------------
public override List<DataValue> GetRowValues(uint rowPos, int maxColumns = 0)
{
ClearErrorState();
List<DataValue> values = new List<DataValue>();
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(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
public override void SetActiveCell(uint colPos, uint rowPos)
{
// ToDo SetActiveCell.
SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
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(SysUtil.Fn, 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(SysUtil.Fn, 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(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return "";
}
public override String GetCellText(uint colPos, uint rowPos, bool trimWhitespace = true)
{
ClearErrorState();
try
{
SaMsXmlElement cellElement = GetCellElement(colPos, rowPos);
return GetCellText(cellElement, true);
}
catch (Exception exc) { HandleException(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return value;
}
public override DataValue GetCellValue(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(SysUtil.Fn, 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(SysUtil.Fn, 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(SysUtil.Fn, exc); }
return new SaMsXmlElement(null);
}
//-------------------------------------------------------------------
public override bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelStyleAttr attr)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
public override bool SetCellValue(uint colPos, uint rowPos, DataValue value, SaMsExcelStyleAttr attr)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// 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(SysUtil.Fn, 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, SaMsExcelStyleAttr attr)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
// Add image to sheet.
public override bool InsertImage(SaMsExcelImage_Config imageConfig)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool FreezePanes(uint colPos, uint rowPos)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool AutoFitRows(SaMsExcelRange range)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
public override bool SelectRange(SaMsExcelRange range)
{
return SetErrorState(SysUtil.Fn + " " + Msg_NotSupported);
}
//-------------------------------------------------------------------
}
//=======================================================================
}