910 lines
32 KiB
C#
910 lines
32 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 !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<DataValue> GetRowDataValues (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("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<Object> values)
|
|
{
|
|
return SetErrorState("Not supported");
|
|
}
|
|
public override bool SetRangeFormulas (SaMsExcelRange range, List<Object> 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");
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
|
|
//=======================================================================
|
|
}
|