blob: a87918deaea25f5e2e01ddb06b5fc11a6184fe41 [file] [log] [blame]
/***************************************************************************
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.");
}
}
}