using System;
using System.Collections.Generic;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
using DocumentFormat.OpenXml.Spreadsheet;
using NPOI.SS.UserModel;
using NPOI.SS.Formula.Functions;
using System.Text.RegularExpressions;
namespace Sarand.Net.Standard.Proc.Excel
{
//=======================================================================
// Some Excel enumerated values are replaced with local integers or
// equivalent enum to avoid errors related to:
// "Cannot be used across assembly boundaries because it has a
// generic type parameter that is an embedded interop type."
// eg. SaMsExcelItemAttr.HorizontalAlignment: short instead of XlHAlign.
//=======================================================================
public class SaMsExcelRange
{
public uint ColFromPos { get; set; } = 0;
public uint ColToPos { get; set; } = 0;
public uint RowFromPos { get; set; } = 0;
public uint RowToPos { get; set; } = 0;
public SaMsExcelRange() { }
public SaMsExcelRange(uint colPos, uint rowPos)
{
ColFromPos = colPos;
ColToPos = colPos;
RowFromPos = rowPos;
RowToPos = rowPos;
}
public SaMsExcelRange(uint colPosFrom, uint rowPosFrom, uint colPosTo, uint rowPosTo)
{
ColFromPos = colPosFrom;
ColToPos = colPosTo;
RowFromPos = rowPosFrom;
RowToPos = rowPosTo;
}
}
//=======================================================================
public class SaMsExcelPageSetup_Config
{
public SaMsExcelPageSetup_Config()
{
}
/// Orientation: xlPortrait (1) or xlLandscape (2). Default: Not specified (ignore)
public SaXlPageOrientation Orientation { get; set; } = SaXlPageOrientation.NotSpecified;
/// Logical unit. Default: Point.
public SaExcelLogicalUnit Unit { get; set; } = SaExcelLogicalUnit.Point;
// Headers and footers.
// NB Font & size and page no and total pages can be encoded as: '&"Batang,Regular"&14Page &P of &N'
public String HeaderLeft { get; set; } = "";
public String HeaderCenter { get; set; } = "";
public String HeaderRight { get; set; } = "";
public String FooterLeft { get; set; } = "";
public String FooterCenter { get; set; } = "";
public String FooterRight { get; set; } = "";
// Margins.
// Negative values leave current value (default=-1.0).
public double MarginLeft { get; set; } = -1.0;
public double MarginRight { get; set; } = -1.0;
public double MarginTop { get; set; } = -1.0;
public double MarginBottom { get; set; } = -1.0;
public double MarginHeader { get; set; } = -1.0;
public double MarginFooter { get; set; } = -1.0;
// Negative values to leave current value, zero to clear (default=-1).
public int PrintTitleRowFrom { get; set; } = -1;
public int PrintTitleRowTo { get; set; } = -1;
public int PrintTitleColFrom { get; set; } = -1;
public int PrintTitleColTo { get; set; } = -1;
// Zoom or 'fit to' pages.
/// Zoom: 40 = 40%, default: -1.
public int Zoom { get; set; } = -1;
/// Fit to pages - Tall. 1 = 1 page tall, default = -1. [ignored if zoom > 0]
public int FitToPagesTall { get; set; } = -1;
/// Fit to pages - Wide. 1 = 1 page wide, default = -1. [ignored if zoom > 0]
public int FitToPagesWide { get; set; } = -1;
}
//=======================================================================
public class SaMsExcelColumnHeading_Config
{
// Heading configuration.
/// Pixels per cm. Default 43. Min 10, max 100.
public double ColumnWidthPixelsPerCm { get; set; } = 43.0;
/// First heading column position (from 1). Default 1. Must be > 0.
public uint FirstHeadingColPos { get; set; } = 1;
/// First heading row position (from 1). Default 1. Must be > 0.
public uint FirstHeadingRowPos { get; set; } = 1;
/// First data row position. Reset by SetColumnHeadings().
public uint FirstDataRowPos { get; set; } = 2;
/// Freeze column position - cell in first data row. 0 = don't freeze.
public uint FreezeColPos { get; set; } = 0;
/// Font attributes. Default Arial 9 bold.
public SaMsExcelFontAttr FontAttr { get; } = new SaMsExcelFontAttr() { Name = "Arial", HeightInPoints = 9, Bold = true };
/// Whether to wrap column header cells.
public bool HeadingTextWrap { get; set; } = false;
/// Cell vertical alignment. XlVAlign.xlTop, xlCenter, xlBottom, xlJustify, xlDistributed etc.
public short? VerticalAlignment { get; set; } = null;
/// Cell foreground colour index, ref. XlColorIndex
public SaXlColourIndex ForegroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// Cell foreground RGB colour value, ref. XlRgbColor. Overrides foreground colour index if changed from default value.
public int? ForegroundColourRGB { get; set; } = null;
/// Cell background colour index, ref. XlColorIndex
public SaXlColourIndex BackgroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
public SaXlBorderWeight BorderWeight { get; set; } = SaXlBorderWeight.NotSpecified;
public bool SetColumnWidths { get; set; } = true;
public bool SetColumnAlignment { get; set; } = true;
public bool SetColumnDataTypeFormat { get; set; } = true; // '@' for strings, '#,##0.00' for numeric depending on thousands/decimals
public String CellFormatDate { get; set; } = "d-mmm-yy";
public String CellFormatTime { get; set; } = "hh:mm";
public String CellFormatDateTimeDelim { get; set; } = " ";
}
//=======================================================================
public class SaMsExcelImage_Config
{
public SaMsExcelImage_Config() { }
public String ImagePath { get; set; } = "";
/// Column to insert image. Default: 0 (current position).
public uint ColPos { get; set; } = 0;
/// Row to insert image. Default: 0 (current position).
public uint RowPos { get; set; } = 0;
/// Image dimension units. Default: Point.
public SaExcelLogicalUnit Unit { get; set; } = SaExcelLogicalUnit.Point;
public double TargetWidth { get; set; } = 0.0;
public double TargetHeight { get; set; } = 0.0;
public double MaxWidth { get; set; } = 0.0;
public double MaxHeight { get; set; } = 0.0;
};
//=======================================================================
public class SaMsExcelBorderAttr
{
public SaMsExcelBorderAttr(XlBordersIndex index)
{
Index = index;
}
public bool IsSpecified
{
get
{
// Any attributes specified.
if (Weight != SaXlBorderWeight.NotSpecified) return true;
if (LineStyle != SaXlLineStyle.NotSpecified) return true;
if (ColorIndex != SaXlColourIndex.NotSpecified) return true;
if (Color != null) return true;
return false;
}
}
/// Border position. XlBordersIndex.xlEdgeLeft etc
public XlBordersIndex Index { get; }
/// Border thickness. XlBorderWeight.xlThin, XlBorderWeight.xlMedium etc.
public SaXlBorderWeight Weight { get; set; } = SaXlBorderWeight.NotSpecified;
/// Border line style. XlLineStyle.xlNone, XlLineStyle.xlContinuous, XlLineStyle.xlDash etc.
public SaXlLineStyle LineStyle { get; set; } = SaXlLineStyle.NotSpecified;
/// Border colour index, re. XlColorIndex. Black = 1 etc.
public SaXlColourIndex ColorIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// Border RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.
public int? Color { get; set; } = null;
}
//=======================================================================
public class SaMsExcelFontAttr
{
/// Font name. Arial etc.
public String Name { get; set; } = String.Empty;
/// Font style. Regular etc.
public String FontStyle { get; set; } = String.Empty;
public double? HeightInPoints { get; set; } = null;
/// Font colour index, ref. XlColorIndex.
public SaXlColourIndex ColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// Font RGB colour value, ref. XlRgbColor.
public int? ColourRGB { get; set; } = null;
public bool? Bold { get; set; } = null;
public bool? Strikethrough { get; set; } = null;
public bool? Superscript { get; set; } = null;
public bool? Subscript { get; set; } = null;
public bool? Outline { get; set; } = null;
public bool? Shadow { get; set; } = null;
public SaXlUnderlineStyle? Underline { get; set; } = null;
public void CopyFrom(SaMsExcelFontAttr fontAttr)
{
Name = fontAttr.Name;
FontStyle = fontAttr.FontStyle;
HeightInPoints = fontAttr.HeightInPoints;
ColourIndex = fontAttr.ColourIndex;
ColourRGB = fontAttr.ColourRGB;
Bold = fontAttr.Bold;
Strikethrough = fontAttr.Strikethrough;
Superscript = fontAttr.Superscript;
Subscript = fontAttr.Subscript;
Outline = fontAttr.Outline;
Shadow = fontAttr.Shadow;
Underline = fontAttr.Underline;
}
public bool IsSpecified()
{
if (HeightInPoints != null && HeightInPoints >= SaMsSpreadsheet.FontHeightInPointsMin && HeightInPoints <= SaMsSpreadsheet.FontHeightInPointsMax) return true;
if (ColourRGB != null || ColourIndex != SaXlColourIndex.NotSpecified) return true;
if ((Bold ?? false) || (Strikethrough ?? false) || (Superscript ?? false) || (Subscript ?? false) || (Outline ?? false) || (Shadow ?? false) || (Underline != null)) return true;
return false;
}
}
//=======================================================================
///
/// Style attributes.
///
public class SaMsExcelStyleAttr
{
// Item attributes.
public SaMsExcelFontAttr FontAttr { get; } = new SaMsExcelFontAttr();
// Attributes that are changed from their default (generally null) are set against the specified range.
/// Cell data format. "@"=Text etc.
public String DataFormat { get; set; } = String.Empty;
/// Cell horizontal alignment. XlHAlign.xlHAlignLeft, xlHAlignCenter, xlHAlignRight etc.
public short? HorizontalAlignment { get; set; } = null;
/// Cell indentation. Tabbed - left, right or distributed alignment [0 to 6]
public short? IndentLevel { get; set; } = null;
/// Cell vertical alignment. XlVAlign.xlTop, xlCenter, xlBottom, xlJustify, xlDistributed etc.
public short? VerticalAlignment { get; set; } = null;
/// Whether to allow text to wrap in cells.
public bool? WrapText { get; set; } = null;
/// Whether cells are merged.
public bool? MergeCells { get; set; } = null;
/// Font pattern foreground colour index, ref. XlColorIndex. Black = 1 etc.
public SaXlColourIndex ForegroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// Cell pattern foreground RGB colour value, ref. XlRgbColor. Overrides colour index if changed from default value.
public int? ForegroundColourRGB { get; set; } = null;
/// Cell pattern, ref. XlPattern. xlSolid etc. NB Defaults to Solid if foreground colour specified.
public SaXlPattern InteriorPattern { get; set; } = SaXlPattern.NotSpecified;
/// Cell pattern background colour index, ref. XlColorIndex. xlAutomatic etc.
public SaXlColourIndex BackgroundColourIndex { get; set; } = SaXlColourIndex.NotSpecified;
/// Cell pattern background RBG Colour value, ref. XlRgbColor. Overrides colour index if changed from default value.
public int? BackgroundPatternColour { get; set; } = null;
// Borders.
public SaMsExcelBorderAttr BorderLeft { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeLeft);
public SaMsExcelBorderAttr BorderTop { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeTop);
public SaMsExcelBorderAttr BorderRight { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeRight);
public SaMsExcelBorderAttr BorderBottom { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlEdgeBottom);
public SaMsExcelBorderAttr BorderInsideVertical { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlInsideVertical);
public SaMsExcelBorderAttr BorderInsideHorizontal { get; } = new SaMsExcelBorderAttr(XlBordersIndex.xlInsideHorizontal);
public List Borders
{
get
{
return new List { BorderLeft, BorderTop, BorderRight, BorderBottom };
}
}
}
//=======================================================================
public abstract class SaMsSpreadsheet : SacsDisposableStateItem
{
//-------------------------------------------------------------------
// Definitions.
public static double FontHeightInPointsMin { get; } = 4;
public static double FontHeightInPointsMax { get; } = 48;
public static int NullColour { get; } = 0x4a4d4120; /* AFX_RFX_LONG_PSEUDO_NULL */
public const String Msg_NotSupported = "Not Supported";
public const String Msg_WorkbookNotOpen = "Workbook is not open";
public const String Msg_WorksheetNotOpen = "Worksheet is not open";
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
protected SaMsSpreadsheet()
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Workbook.
public abstract bool IsWorkbookValid();
public abstract bool CreateWorkbook();
public abstract bool OpenWorkbook(String filePath);
/// Cleanse workbook - remove unreferenced styles etc.
public abstract bool CleanseWorkbook();
public abstract bool SaveDocument(String filePath, int fileFormat = (int)SaXlFileFormat.Default /* Default: XlFileFormat.xlOpenXMLWorkbook */, bool bAutomaticallyOverwrite = false);
public abstract bool CloseWorkbook();
public abstract byte[] GetDocumentData();
// Default style.
public abstract void ApplyDefaultStyle(SaMsExcelStyleAttr attr);
//-------------------------------------------------------------------
// Worksheets.
/// Number of worksheets in the workbook.
public abstract uint WorksheetCount { get; }
/// Set the worksheets in the workbook - sheets are added or removed to match the value.
public abstract bool SetWorksheetCount(int worksheetCount);
/// Names of worksheets in the workbook.
public abstract List WorksheetNames { get; }
/// Add a worksheet to the workbook.
public abstract bool AddWorksheet(String name);
/// Remove the nth worksheet - position from 1.
public abstract bool RemoveWorksheet(uint worksheetPos);
//-------------------------------------------------------------------
// Worksheet.
/// Open the nth worksheet - position from 1.
public abstract bool OpenWorksheet(uint worksheetPos);
/// Open the worksheet with the specified name.
public abstract bool OpenWorksheet(String name);
/// Close the current worksheet.
public abstract bool CloseWorksheet();
/// Is the current worksheet valid.
public abstract bool IsWorksheetValid();
/// Get the position of the current worksheet. 0 = not open.
public abstract uint WorksheetPos { get; }
/// Get the name of the current worksheet.
public abstract String WorksheetName { get; set; }
/// Set the default attributes for the entire worksheet
public abstract bool SetWorksheetAttr(SaMsExcelStyleAttr attr);
// Worksheet Page Setup.
/// Configure the Page Setup properties.
public abstract bool SetPageSetup(SaMsExcelPageSetup_Config pageSetupConfig);
/// Configure a page break at the specified cell.
public abstract bool SetPageBreak(uint colPos, uint rowPos);
//-------------------------------------------------------------------
// Columns.
public abstract uint ActiveColPos { get; }
public abstract double GetColWidth(uint colPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
public abstract bool SetColWidth(uint colPos, double fWidth, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
//-------------------------------------------------------------------
// Rows.
public abstract uint FirstRowPos { get; }
public abstract uint LastRowPos { get; }
public abstract uint ActiveRowPos { get; }
public abstract List GetRowValues(uint rowPos, int maxColumns = 0);
public abstract double GetRowHeight(uint rowPos, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
public abstract bool SetRowHeight(uint rowPos, double fHeight, SaExcelLogicalUnit unit = SaExcelLogicalUnit.Point);
//-------------------------------------------------------------------
// Cells.
public abstract void SetActiveCell(uint colPos, uint rowPos);
public abstract String GetCellFormula(uint colPos, uint rowPos);
public abstract String GetCellText(uint colPos, uint rowPos, bool trimWhitespace = true);
public abstract DataValue GetCellValue(uint colPos, uint rowPos);
public abstract bool SetCellFormula(uint colPos, uint rowPos, String value, SaMsExcelStyleAttr attr = null);
public abstract bool SetCellValue(uint colPos, uint rowPos, DataValue value, SaMsExcelStyleAttr attr = null);
/// Set cell formulas across a row - override if more efficient method available.
public virtual bool SetRowFormulas(uint colPosStart, uint rowPos, List values)
{
for (uint colPos = Math.Max(1, colPosStart); colPos <= values.Count; colPos++)
SetCellFormula(colPos, rowPos, values[(int)(colPos - 1)]);
return OK;
}
/// Set cell values across a row - override if more efficient method available.
public virtual bool SetRowValues(uint colPosStart, uint rowPos, List values)
{
for (uint colPos = Math.Max(1, colPosStart); colPos <= values.Count; colPos++)
SetCellValue(colPos, rowPos, values[(int)(colPos - 1)]);
return OK;
}
///
/// Set cell formulas across a range of cells - override if more efficient logic available.
/// - Values will wrap if more cells to fill than values.
public virtual bool SetRangeFormulas(SaMsExcelRange range, List values)
{
if (values.IsEmpty()) return SetErrorState(SysUtil.Fn + " No values supplied");
int valueIdx = 0;
for (uint colPos = Math.Max(1, range.ColFromPos); colPos <= range.ColToPos; colPos++)
{
for (uint rowPos = Math.Max(1, range.RowFromPos); rowPos <= range.RowToPos; rowPos++)
{
// Wrap values if more cells to fill.
if (valueIdx >= values.Count)
valueIdx = 0;
SetCellFormula(colPos, rowPos, values[valueIdx++]);
}
}
return OK;
}
///
/// Set cell values across a range of cells - override if more efficient logic available.
/// - Values will wrap if more cells to fill than values.
public virtual bool SetRangeValues(SaMsExcelRange range, List values)
{
if (values.IsEmpty()) return SetErrorState(SysUtil.Fn + " No values supplied");
int valueIdx = 0;
for (uint colPos = Math.Max(1, range.ColFromPos); colPos <= range.ColToPos; colPos++)
{
for (uint rowPos = Math.Max(1, range.RowFromPos); rowPos <= range.RowToPos; rowPos++)
{
// Wrap values if more cells to fill.
if (valueIdx >= values.Count)
valueIdx = 0;
SetCellValue(colPos, rowPos, values[valueIdx++]);
}
}
return OK;
}
//-------------------------------------------------------------------
// Populate headings.
public bool SetColumnHeadings(List columns, SaMsExcelColumnHeading_Config headingConfig)
{
ClearErrorState();
double columnWidthPixelsPerCm = Math.Min(100.0, Math.Max(10.0, headingConfig.ColumnWidthPixelsPerCm));
uint firstHeadingRowPos = (uint)Math.Max(1, headingConfig.FirstHeadingRowPos);
// Assume only a single header row.
headingConfig.FirstDataRowPos = firstHeadingRowPos + 1;
try
{
// Populate default heading cell attributes.
SaMsExcelStyleAttr attrHdrDefault = new SaMsExcelStyleAttr();
attrHdrDefault.FontAttr.CopyFrom(headingConfig.FontAttr);
attrHdrDefault.WrapText = headingConfig.HeadingTextWrap ? true : null;
attrHdrDefault.VerticalAlignment = headingConfig.VerticalAlignment;
attrHdrDefault.ForegroundColourIndex = headingConfig.ForegroundColourIndex;
attrHdrDefault.BackgroundColourIndex = headingConfig.BackgroundColourIndex;
if (headingConfig.ForegroundColourRGB != null)
attrHdrDefault.ForegroundColourRGB = headingConfig.ForegroundColourRGB;
if (headingConfig.BorderWeight != SaXlBorderWeight.NotSpecified)
{
attrHdrDefault.BorderLeft.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderTop.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderRight.Weight = headingConfig.BorderWeight;
attrHdrDefault.BorderBottom.Weight = headingConfig.BorderWeight;
}
SaMsExcelRange rangeHeadings = new SaMsExcelRange(headingConfig.FirstHeadingColPos, firstHeadingRowPos, (uint)(headingConfig.FirstHeadingColPos + columns.Count - 1), firstHeadingRowPos);
SetRangeAttr(rangeHeadings, attrHdrDefault);
// Set default column attributes.
bool defaultColumnAttributesPopulated = false;
SaMsExcelStyleAttr attrColDefault = new SaMsExcelStyleAttr();
if (headingConfig.SetColumnAlignment)
{
attrColDefault.HorizontalAlignment = (short)XlHAlign.xlHAlignLeft;
defaultColumnAttributesPopulated = true;
}
if (headingConfig.SetColumnDataTypeFormat)
{
attrColDefault.DataFormat = "@"; // '@' = 'Text' format.
defaultColumnAttributesPopulated = true;
}
if (defaultColumnAttributesPopulated)
{
SaMsExcelRange rangeColumns = new SaMsExcelRange(headingConfig.FirstHeadingColPos, 0, (uint)(headingConfig.FirstHeadingColPos + columns.Count - 1), 0);
SetRangeAttr(rangeColumns, attrColDefault);
}
// Populate column styling and headings.
List columnHeadings = new List();
for (int columnIdx = 0; columnIdx < columns.Count; columnIdx++)
{
ColumnDfn column = columns[columnIdx];
uint colPos = (uint)(Math.Max(1, headingConfig.FirstHeadingColPos) + columnIdx);
SaMsExcelRange range = new SaMsExcelRange(colPos, firstHeadingRowPos);
// Specific heading cell attributes.
bool headingCellHasSpecificAttr = false;
SaMsExcelStyleAttr attrHdr = new SaMsExcelStyleAttr();
if (headingCellHasSpecificAttr)
SetRangeAttr(range, attrHdr);
// Column width and caption:
if (headingConfig.SetColumnWidths) SetColWidth(colPos, column.Width * 1.5 / columnWidthPixelsPerCm, SaExcelLogicalUnit.Cm);
columnHeadings.Add(column.Caption);
// Column attributes:
bool columnHasSpecificStyle = false;
SaMsExcelStyleAttr attrCol = new SaMsExcelStyleAttr();
if (headingConfig.SetColumnAlignment && column.Alignment != ColumnAlignment.Left && column.Alignment != ColumnAlignment.Default)
{
columnHasSpecificStyle = true;
switch (column.Alignment)
{
case ColumnAlignment.Center: attrCol.HorizontalAlignment = (short)XlHAlign.xlHAlignCenter; break;
case ColumnAlignment.Right: attrCol.HorizontalAlignment = (short)XlHAlign.xlHAlignRight; break;
}
}
if (column.WrapText)
{
columnHasSpecificStyle = true;
attrCol.WrapText = true;
}
if (headingConfig.SetColumnDataTypeFormat && column.DataType != DataValue.DataType.String)
{
columnHasSpecificStyle = true;
if (column.DataType == DataValue.DataType.Long || column.DataType == DataValue.DataType.Double || column.DataType == DataValue.DataType.Decimal)
{
// Numeric format: "#,##0.00"
String numberFormat = "0";
if (column.NumThousands != '\0')
{
numberFormat = "###" + numberFormat;
numberFormat = numberFormat.Insert(1, column.NumThousands.ToString()); // Ends up as '#,##0'.
}
if ((column.DataType == DataValue.DataType.Double || column.DataType == DataValue.DataType.Decimal) && column.NumDecimals > 0)
numberFormat += "." + "0".PadRight(column.NumDecimals, '0');
attrCol.DataFormat = numberFormat + ";[Red]-" + numberFormat;
}
else if (column.DataType == DataValue.DataType.Date) attrCol.DataFormat = headingConfig.CellFormatDate;
else if (column.DataType == DataValue.DataType.Time) attrCol.DataFormat = headingConfig.CellFormatTime;
else if (column.DataType == DataValue.DataType.DateTime) attrCol.DataFormat = headingConfig.CellFormatDate + headingConfig.CellFormatDateTimeDelim + headingConfig.CellFormatTime;
}
if (columnHasSpecificStyle)
SetRangeAttr(new SaMsExcelRange(colPos, 0), attrCol);
// Freeze the window below if a colmn has been specified:
if (headingConfig.FreezeColPos > 0) FreezePanes(headingConfig.FreezeColPos, firstHeadingRowPos + 1);
}
// Populate header captions.
SetRowValues(headingConfig.FirstHeadingColPos, firstHeadingRowPos, columnHeadings);
// Auto fit header row if text is wrapped:
if (headingConfig.HeadingTextWrap)
AutoFitRows(rangeHeadings);
}
catch (Exception exc) { HandleException(SysUtil.Fn, exc); }
return OK;
}
//-------------------------------------------------------------------
// Cell attributes.
public abstract bool SetRangeAttr(SaMsExcelRange range, SaMsExcelStyleAttr attr);
// Add image to sheet.
public abstract bool InsertImage(SaMsExcelImage_Config imageConfig);
/// Freeze sheet at specified cell.
public abstract bool FreezePanes(uint colPos, uint rowPos);
/// Auto-fit row heights to contents.
public abstract bool AutoFitRows(SaMsExcelRange range);
/// Select range of cells.
public abstract bool SelectRange(SaMsExcelRange range);
//-------------------------------------------------------------------
// Utilities.
///
/// Convert cell range to Id.
/// - Not fixed: "A9:AB34"
/// - Fixed cols: "$A9:$AB34"
/// - Fixed rows: "A$9:AB$34"
/// - Fixed: "$A$9:$AB$34"
///
public static String GetRangeId(SaMsExcelRange range, bool fixedCols = false, bool fixedRows = false)
{
return GetColumnIdPrefix(range.ColFromPos, fixedCols) + GetRowIdSuffix(range.RowFromPos, fixedRows) + ":" + GetColumnIdPrefix(range.ColToPos, fixedCols) + GetRowIdSuffix(range.RowToPos, fixedRows);
}
///
/// Convert column position to Id prefix. 1=A,2=B,3=C...26=Z,27=AA,28=AB etc.
/// - Fixed prefixes with '$'.
///
public static String GetColumnIdPrefix(uint colPos, bool fixedCol = false)
{
uint dividend = colPos;
string columnName = String.Empty;
uint modulo = 0;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (uint)((dividend - modulo) / 26);
}
return (fixedCol ? "$" : "") + columnName;
}
/// Convert column Id to position. A=1,B=2,C=3...Z=26,AA=27 etc.
public static int GetPosForColId(String colId)
{
colId = StringUtil.TrimWhitespace(StringUtil.OnBlank(colId)).ToUpper();
if (!colId.IsMatch("^[A-Z]{1,2}$"))
{
SaLogger.Warn(SysUtil.Fn + " Invalid column Id supplied: '" + colId + "'");
return 0;
}
int col = (colId[0] - 'A' + 1);
if (colId.Length > 1)
col = (col * 26) + (colId[1] - 'A' + 1);
return col;
}
/// Column Id - To reference entire column: A:A,B:A,C:A...Z:Z,AA:AA etc.
public static String GetColumnId(uint colPos)
{
return GetColumnIdPrefix(colPos) + ":" + GetColumnIdPrefix(colPos);
}
///
/// Convert row pos to id suffix. 1=1,2=2,3=3...26=26,27=27,28=28 etc.
/// - Fixed prefixes with '$'.
///
public static String GetRowIdSuffix(uint rowPos, bool fixedRow = false)
{
return (rowPos > 0 ? (fixedRow ? "$" : "") + rowPos.ToString() : "");
}
/// Row id - To reference entire row: 1:1,2:2,3:3 etc.
public static String GetRowId(uint rowPos)
{
return GetRowIdSuffix(rowPos) + ":" + GetRowIdSuffix(rowPos);
}
/// Convert cell by column and row positions to Id.
public static String GetCellId(uint colPos, uint rowPos)
{
return GetColumnIdPrefix(colPos) + GetRowIdSuffix(rowPos);
}
public static DateTime ExtractDate(String value)
{
DateTime date = DateTime.MinValue;
// TODO SaMsSpreadsheet.ExtractDate
return date;
}
//-------------------------------------------------------------------
// L e n g t h U n i t C o n v e r s i o n
//-------------------------------------------------------------------
// Points
// - 72 per inch
// EMU - English Metric Unit
// - 914400 EMUs per inch
// - EMU = inch * 914400
// - EMU = pixel * 914400 / 96 (assuming 96 dpi)
//-------------------------------------------------------------------
public static double PointsPerInch { get; } = 72;
public static double PixelsPerInch { get; } = 96;
public static double CmsPerInch { get; } = 2.54;
public double ConvertLength(double fLength, SaExcelLogicalUnit eUnitFrom, SaExcelLogicalUnit eUnitTo, bool bVertical)
{
// Convert length to points:
double fLengthPoints = fLength;
if (eUnitFrom == SaExcelLogicalUnit.Pixel) fLengthPoints = PixelsToPoints(fLength, bVertical);
else if (eUnitFrom == SaExcelLogicalUnit.Cm) fLengthPoints = CmsToPoints(fLength);
else if (eUnitFrom == SaExcelLogicalUnit.Inch) fLengthPoints = InchesToPoints(fLength);
// Convert points to target units:
double fLengthTarget = fLengthPoints;
if (eUnitTo == SaExcelLogicalUnit.Pixel) fLengthTarget = PointsToPixels(fLengthPoints, bVertical);
else if (eUnitTo == SaExcelLogicalUnit.Cm) fLengthTarget = PointsToCms(fLengthPoints);
else if (eUnitTo == SaExcelLogicalUnit.Inch) fLengthTarget = PointsToInches(fLengthPoints);
return fLengthTarget;
}
public double ToPoints(double fLength, SaExcelLogicalUnit eUnitFrom, bool bVertical)
{
return ConvertLength(fLength, eUnitFrom, SaExcelLogicalUnit.Point, bVertical);
}
public double FromPoints(double fLength, SaExcelLogicalUnit eUnitTo, bool bVertical)
{
return ConvertLength(fLength, SaExcelLogicalUnit.Point, eUnitTo, bVertical);
}
public virtual double PointsToPixels(double points, bool bVertical)
{
// Pixel size depends on monitor resolution.
// REf. https://stackoverflow.com/questions/139655/convert-pixels-to-points
// Assume 96 pixels per inch.
return points * PixelsPerInch / PointsPerInch;
}
public virtual double PointsToCms(double points)
{
return PointsToInches(points) * CmsPerInch;
}
public virtual double PointsToInches(double points)
{
return points / PointsPerInch;
}
public virtual double PixelsToPoints(double pixels, bool bVertical)
{
// Pixel size depends on monitor resolution.
// REf. https://stackoverflow.com/questions/139655/convert-pixels-to-points
// Assume 96 pixels per inch.
return pixels / PixelsPerInch * PointsPerInch;
}
public virtual double CmsToPoints(double cms)
{
return InchesToPoints(cms / CmsPerInch);
}
public virtual double InchesToPoints(double inches)
{
return inches * PointsPerInch;
}
//-------------------------------------------------------------------
protected void HandleException(String fn, Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
SetErrorState("Problems were encountered interfacing to Excel - " + ExceptionUtil.GetErrorStateMessage(exc));
}
//-------------------------------------------------------------------
}
//=======================================================================
}