| /*************************************************************************** | |
| Copyright (c) Microsoft Corporation 2012-2015. | |
| This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: | |
| http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx | |
| Published at http://OpenXmlDeveloper.org | |
| Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx | |
| Developer: Eric White | |
| Blog: http://www.ericwhite.com | |
| Twitter: @EricWhiteDev | |
| Email: eric@ericwhite.com | |
| ***************************************************************************/ | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using System.Xml.Linq; | |
| using DocumentFormat.OpenXml.Packaging; | |
| using System.IO; | |
| namespace OpenXmlPowerTools | |
| { | |
| public class Table | |
| { | |
| public int Id { get; set; } | |
| public string TableName { get; set; } | |
| public string DisplayName { get; set; } | |
| public XElement TableStyleInfo { get; set; } | |
| public string Ref { get; set; } | |
| public int LeftColumn { get; set; } | |
| public int RightColumn { get; set; } | |
| public int TopRow { get; set; } | |
| public int BottomRow { get; set; } | |
| public int? HeaderRowCount { get; set; } | |
| public int? TotalsRowCount { get; set; } | |
| public string TableType { get; set; } // external data query, data in worksheet, or XML data | |
| public TableDefinitionPart TableDefinitionPart { get; set; } | |
| public WorksheetPart Parent { get; set; } | |
| public Table(WorksheetPart parent) { Parent = parent; } | |
| public IEnumerable<TableColumn> TableColumns() | |
| { | |
| XNamespace x = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; | |
| return TableDefinitionPart | |
| .GetXDocument() | |
| .Root | |
| .Element(x + "tableColumns") | |
| .Elements(x + "tableColumn") | |
| .Select((c, i) => | |
| new TableColumn(this) | |
| { | |
| Id = (int)c.Attribute("id"), | |
| ColumnNumber = this.LeftColumn + i, | |
| Name = (string)c.Attribute("name"), | |
| DataDxfId = (int?)c.Attribute("dataDxfId"), | |
| QueryTableFieldId = (int?)c.Attribute("queryTableFieldId"), | |
| UniqueName = (string)c.Attribute("uniqueName"), | |
| ColumnIndex = i, | |
| } | |
| ); | |
| } | |
| public IEnumerable<TableRow> TableRows() | |
| { | |
| string refStart = Ref.Split(':').First(); | |
| int rowStart = Int32.Parse(XlsxTables.SplitAddress(refStart)[1]); | |
| string refEnd = Ref.Split(':').ElementAt(1); | |
| int rowEnd = Int32.Parse(XlsxTables.SplitAddress(refEnd)[1]); | |
| int headerRowsCount = HeaderRowCount == null ? 0 : (int)HeaderRowCount; | |
| int totalRowsCount = TotalsRowCount == null ? 0 : (int)TotalsRowCount; | |
| return Parent | |
| .Rows() | |
| .Skip(headerRowsCount) | |
| .SkipLast(totalRowsCount) | |
| .Where(r => | |
| { | |
| int rowId = Int32.Parse(r.RowId); | |
| return rowId >= rowStart && rowId <= rowEnd; | |
| } | |
| ) | |
| .Select(r => new TableRow(this) { Row = r }); | |
| } | |
| } | |
| public class TableColumn | |
| { | |
| public int Id { get; set; } | |
| public string Name { get; set; } | |
| public int? DataDxfId { get; set; } | |
| public int? QueryTableFieldId { get; set; } | |
| public string UniqueName { get; set; } | |
| public int ColumnNumber { get; set; } | |
| public int ColumnIndex { get; set; } | |
| public Table Parent { get; set; } | |
| public TableColumn(Table parent) { Parent = parent; } | |
| } | |
| public class TableRow | |
| { | |
| public Row Row { get; set; } | |
| public Table Parent { get; set; } | |
| public TableRow(Table parent) { Parent = parent; } | |
| public TableCell this[string columnName] | |
| { | |
| get | |
| { | |
| TableColumn tc = Parent | |
| .TableColumns() | |
| .Where(x => x.Name.ToLower() == columnName.ToLower()) | |
| .FirstOrDefault(); | |
| if (tc == null) | |
| throw new Exception("Invalid column name: " + columnName); | |
| string[] refs = Parent.Ref.Split(':'); | |
| string[] startRefs = XlsxTables.SplitAddress(refs[0]); | |
| string columnAddress = XlsxTables.IndexToColumnAddress(XlsxTables.ColumnAddressToIndex(startRefs[0]) + tc.ColumnIndex); | |
| Cell cell = Row.Cells().Where(c => c.ColumnAddress == columnAddress).FirstOrDefault(); | |
| if (cell != null) | |
| { | |
| if (cell.Type == "s") | |
| return new TableCell(cell.SharedString); | |
| else | |
| return new TableCell(cell.Value); | |
| } | |
| else | |
| return new TableCell(""); | |
| } | |
| } | |
| } | |
| public class TableCell : IEquatable<TableCell> | |
| { | |
| public string Value { get; set; } | |
| public TableCell(string v) | |
| { | |
| Value = v; | |
| } | |
| public override string ToString() | |
| { | |
| return Value; | |
| } | |
| public override bool Equals(object obj) | |
| { | |
| return this.Value == ((TableCell)obj).Value; | |
| } | |
| bool IEquatable<TableCell>.Equals(TableCell other) | |
| { | |
| return this.Value == other.Value; | |
| } | |
| public override int GetHashCode() | |
| { | |
| return this.Value.GetHashCode(); | |
| } | |
| public static bool operator ==(TableCell left, TableCell right) | |
| { | |
| if ((object)left != (object)right) return false; | |
| return left.Value == right.Value; | |
| } | |
| public static bool operator !=(TableCell left, TableCell right) | |
| { | |
| if ((object)left != (object)right) return false; | |
| return left.Value != right.Value; | |
| } | |
| public static explicit operator string(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return cell.Value; | |
| } | |
| public static explicit operator bool(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return cell.Value == "1"; | |
| } | |
| public static explicit operator bool?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return cell.Value == "1"; | |
| } | |
| public static explicit operator int(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return Int32.Parse(cell.Value); | |
| } | |
| public static explicit operator int?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return Int32.Parse(cell.Value); | |
| } | |
| public static explicit operator uint(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return UInt32.Parse(cell.Value); | |
| } | |
| public static explicit operator uint?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return UInt32.Parse(cell.Value); | |
| } | |
| public static explicit operator long(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return Int64.Parse(cell.Value); | |
| } | |
| public static explicit operator long?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return Int64.Parse(cell.Value); | |
| } | |
| public static explicit operator ulong(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return UInt64.Parse(cell.Value); | |
| } | |
| public static explicit operator ulong?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return UInt64.Parse(cell.Value); | |
| } | |
| public static explicit operator float(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return Single.Parse(cell.Value); | |
| } | |
| public static explicit operator float?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return Single.Parse(cell.Value); | |
| } | |
| public static explicit operator double(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return Double.Parse(cell.Value); | |
| } | |
| public static explicit operator double?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return Double.Parse(cell.Value); | |
| } | |
| public static explicit operator decimal(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return Decimal.Parse(cell.Value); | |
| } | |
| public static explicit operator decimal?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return Decimal.Parse(cell.Value); | |
| } | |
| public static implicit operator DateTime(TableCell cell) | |
| { | |
| if (cell == null) throw new ArgumentNullException("TableCell"); | |
| return new DateTime(1900, 1, 1).AddDays(Int32.Parse(cell.Value) - 2); | |
| } | |
| public static implicit operator DateTime?(TableCell cell) | |
| { | |
| if (cell == null) return null; | |
| return new DateTime(1900, 1, 1).AddDays(Int32.Parse(cell.Value) - 2); | |
| } | |
| } | |
| public class Row | |
| { | |
| public XElement RowElement { get; set; } | |
| public string RowId { get; set; } | |
| public string Spans { get; set; } | |
| public List<Cell> Cells() | |
| { | |
| XNamespace s = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; | |
| SpreadsheetDocument doc = (SpreadsheetDocument)Parent.OpenXmlPackage; | |
| SharedStringTablePart sharedStringTable = doc.WorkbookPart.SharedStringTablePart; | |
| IEnumerable<XElement> cells = this.RowElement.Elements(S.c); | |
| var r = cells | |
| .Select(cell => { | |
| var cellType = (string)cell.Attribute("t"); | |
| var sharedString = cellType == "s" ? | |
| sharedStringTable | |
| .GetXDocument() | |
| .Root | |
| .Elements(s + "si") | |
| .Skip((int)cell.Element(s + "v")) | |
| .First() | |
| .Descendants(s + "t") | |
| .StringConcatenate(e => (string)e) | |
| : null; | |
| var column = (string)cell.Attribute("r"); | |
| var columnAddress = column.Split('0', '1', '2', '3', '4', '5', '6', '7', '8', '9').First(); | |
| var columnIndex = XlsxTables.ColumnAddressToIndex(columnAddress); | |
| var newCell = new Cell(this) | |
| { | |
| CellElement = cell, | |
| Row = (string)RowElement.Attribute("r"), | |
| Column = column, | |
| ColumnAddress = columnAddress, | |
| ColumnIndex = columnIndex, | |
| Type = cellType, | |
| Formula = (string)cell.Element(S.f), | |
| Style = (int?)cell.Attribute("s"), | |
| Value = (string)cell.Element(S.v), | |
| SharedString = sharedString | |
| }; | |
| return newCell; | |
| }); | |
| var ra = r.ToList(); | |
| return ra; | |
| } | |
| public WorksheetPart Parent { get; set; } | |
| public Row(WorksheetPart parent) { Parent = parent; } | |
| } | |
| public class Cell | |
| { | |
| public XElement CellElement { get; set; } | |
| public string Row { get; set; } | |
| public string Column { get; set; } | |
| public string ColumnAddress { get; set; } | |
| public int ColumnIndex { get; set; } | |
| public string Type { get; set; } | |
| public string Value { get; set; } | |
| public string Formula { get; set; } | |
| public int? Style { get; set; } | |
| public string SharedString { get; set; } | |
| public Row Parent { get; set; } | |
| public Cell(Row parent) { Parent = parent; } | |
| } | |
| public static class XlsxTables | |
| { | |
| public static IEnumerable<Table> Tables(this SpreadsheetDocument spreadsheet) | |
| { | |
| foreach (var worksheetPart in spreadsheet.WorkbookPart.WorksheetParts) | |
| foreach (var table in worksheetPart.TableDefinitionParts) | |
| { | |
| XDocument tableDefDoc = table.GetXDocument(); | |
| Table t = new Table(worksheetPart) | |
| { | |
| Id = (int)tableDefDoc.Root.Attribute("id"), | |
| TableName = (string)tableDefDoc.Root.Attribute("name"), | |
| DisplayName = (string)tableDefDoc.Root.Attribute("displayName"), | |
| TableStyleInfo = tableDefDoc.Root.Element(S.tableStyleInfo), | |
| Ref = (string)tableDefDoc.Root.Attribute("ref"), | |
| TotalsRowCount = (int?)tableDefDoc.Root.Attribute("totalsRowCount"), | |
| //HeaderRowCount = (int?)tableDefDoc.Root.Attribute("headerRowCount"), | |
| HeaderRowCount = 1, // currently there always is a header row | |
| TableType = (string)tableDefDoc.Root.Attribute("tableType"), | |
| TableDefinitionPart = table | |
| }; | |
| int leftColumn, topRow, rightColumn, bottomRow; | |
| ParseRange(t.Ref, out leftColumn, out topRow, out rightColumn, out bottomRow); | |
| t.LeftColumn = leftColumn; | |
| t.TopRow = topRow; | |
| t.RightColumn = rightColumn; | |
| t.BottomRow = bottomRow; | |
| yield return t; | |
| } | |
| } | |
| public static void ParseRange(string theRef, out int leftColumn, out int topRow, out int rightColumn, out int bottomRow) | |
| { | |
| // C5:E7 | |
| var spl = theRef.Split(':'); | |
| string refStart = spl.First(); | |
| var refStartSplit = XlsxTables.SplitAddress(refStart); | |
| leftColumn = XlsxTables.ColumnAddressToIndex(refStartSplit[0]); | |
| topRow = Int32.Parse(refStartSplit[1]); | |
| string refEnd = spl.ElementAt(1); | |
| var refEndSplit = XlsxTables.SplitAddress(refEnd); | |
| rightColumn = XlsxTables.ColumnAddressToIndex(refEndSplit[0]); | |
| bottomRow = Int32.Parse(refEndSplit[1]); | |
| } | |
| public static Table Table(this SpreadsheetDocument spreadsheet, | |
| string tableName) | |
| { | |
| return spreadsheet.Tables().Where(t => t.TableName.ToLower() == tableName.ToLower()).FirstOrDefault(); | |
| } | |
| public static IEnumerable<Row> Rows(this WorksheetPart worksheetPart) | |
| { | |
| XNamespace s = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; | |
| var rows = worksheetPart | |
| .GetXDocument() | |
| .Root | |
| .Elements(S.sheetData) | |
| .Elements(S.row) | |
| .Select(r => | |
| { | |
| var row = new Row(worksheetPart) | |
| { | |
| RowElement = r, | |
| RowId = (string)r.Attribute("r"), | |
| Spans = (string)r.Attribute("spans") | |
| }; | |
| return row; | |
| }); | |
| return rows; | |
| } | |
| public static string[] SplitAddress(string address) | |
| { | |
| int i; | |
| for (i = 0; i < address.Length; i++) | |
| if (address[i] >= '0' && address[i] <= '9') | |
| break; | |
| if (i == address.Length) | |
| throw new FileFormatException("Invalid spreadsheet. Bad cell address."); | |
| return new[] { | |
| address.Substring(0, i), | |
| address.Substring(i) | |
| }; | |
| } | |
| public static string IndexToColumnAddress(int index) | |
| { | |
| if (index < 26) | |
| { | |
| char c = (char)((int)'A' + index); | |
| string s = new string(c, 1); | |
| return s; | |
| } | |
| if (index < 702) | |
| { | |
| int i = index - 26; | |
| int i1 = (int)(i / 26); | |
| int i2 = i % 26; | |
| string s = new string((char)((int)'A' + i1), 1) + | |
| new string((char)((int)'A' + i2), 1); | |
| return s; | |
| } | |
| if (index < 18278) | |
| { | |
| int i = index - 702; | |
| int i1 = (int)(i / 676); | |
| i = i - i1 * 676; | |
| int i2 = (int)(i / 26); | |
| int i3 = i % 26; | |
| string s = new string((char)((int)'A' + i1), 1) + | |
| new string((char)((int)'A' + i2), 1) + | |
| new string((char)((int)'A' + i3), 1); | |
| return s; | |
| } | |
| throw new Exception("Invalid column address"); | |
| } | |
| public static int ColumnAddressToIndex(string columnAddress) | |
| { | |
| if (columnAddress.Length == 1) | |
| { | |
| char c = columnAddress[0]; | |
| int i = c - 'A'; | |
| return i; | |
| } | |
| if (columnAddress.Length == 2) | |
| { | |
| char c1 = columnAddress[0]; | |
| char c2 = columnAddress[1]; | |
| int i1 = c1 - 'A'; | |
| int i2 = c2 - 'A'; | |
| return (i1 + 1) * 26 + i2; | |
| } | |
| if (columnAddress.Length == 3) | |
| { | |
| char c1 = columnAddress[0]; | |
| char c2 = columnAddress[1]; | |
| char c3 = columnAddress[2]; | |
| int i1 = c1 - 'A'; | |
| int i2 = c2 - 'A'; | |
| int i3 = c3 - 'A'; | |
| return (i1 + 1) * 676 + (i2 + 1) * 26 + i3; | |
| } | |
| throw new FileFormatException("Invalid spreadsheet: Invalid column address."); | |
| } | |
| } | |
| } |