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)); } //------------------------------------------------------------------- } //======================================================================= }