[NWD] More EPPlus improvements. - Revamp save code to avoid memory copies - Remove more unnecessary code for mutating sheets - General cleanup Change-Id: I9faf6c7cc94a50600076bd42ac456ba6f96cd407 Reviewed-on: https://gnocchi-internal-review.git.corp.google.com/c/third_party/epplus/+/208000 Reviewed-by: Hughes Hilton <hugheshilton@google.com>
diff --git a/EPPlus/CellStore.cs b/EPPlus/CellStore.cs index a00f890..684cf3b 100644 --- a/EPPlus/CellStore.cs +++ b/EPPlus/CellStore.cs
@@ -51,16 +51,7 @@ } internal class ColumnIndex : IndexBase { - internal IndexBase _searchIx = new(); - - public ColumnIndex() { - _pages = new PageIndex[CellStore<int>._pagesPerColumnMin]; - PageCount = 0; - } - - ~ColumnIndex() { - _pages = null; - } + private readonly IndexBase _searchIx = new(); internal int GetPosition(int row) { var page = (short)(row >> CellStore<int>._pageBits); @@ -150,20 +141,12 @@ return -1; } - internal int FindNext(int page) { - var p = GetPosition(page); - if (p < 0) { - return ~p; - } - return p; - } - - internal PageIndex[] _pages; + internal PageIndex[] _pages = new PageIndex[CellStore<int>._pagesPerColumnMin]; internal int PageCount; } internal class PageIndex : IndexBase { - internal IndexBase _searchIx = new(); + private readonly IndexBase _searchIx = new(); public PageIndex() { Rows = new IndexItem[CellStore<int>._pageSizeMin]; @@ -186,10 +169,6 @@ Offset = offset; } - ~PageIndex() { - Rows = null; - } - internal int Offset; internal int IndexOffset => IndexExpanded + Offset; @@ -262,14 +241,10 @@ internal const int _pagesPerColumnMin = 32; private List<T> _values = new(); - internal ColumnIndex[] _columnIndex; + internal ColumnIndex[] _columnIndex = new ColumnIndex[_colSizeMin]; internal IndexBase _searchIx = new(); internal int ColumnCount; - public CellStore() { - _columnIndex = new ColumnIndex[_colSizeMin]; - } - ~CellStore() { if (_values != null) { _values.Clear(); @@ -290,34 +265,6 @@ : Array.BinarySearch(_columnIndex, 0, ColumnCount, _searchIx); } - internal CellStore<T> Clone() { - int row, - col; - var ret = new CellStore<T>(); - for (int c = 0; c < ColumnCount; c++) { - col = _columnIndex[c].Index; - for (int p = 0; p < _columnIndex[c].PageCount; p++) { - for (int r = 0; r < _columnIndex[c]._pages[p].RowCount; r++) { - row = _columnIndex[c]._pages[p].IndexOffset + _columnIndex[c]._pages[p].Rows[r].Index; - ret.SetValue(row, col, _values[_columnIndex[c]._pages[p].Rows[r].IndexPointer]); - } - } - } - return ret; - } - - internal int Count { - get { - int count = 0; - for (int c = 0; c < ColumnCount; c++) { - for (int p = 0; p < _columnIndex[c].PageCount; p++) { - count += _columnIndex[c]._pages[p].RowCount; - } - } - return count; - } - } - internal bool GetDimension(out int fromRow, out int fromCol, out int toRow, out int toCol) { if (ColumnCount == 0) { fromRow = fromCol = toRow = toCol = 0; @@ -403,45 +350,6 @@ return _values[i]; } return default(T); - //var col = GetPosition(Column); - //if (col >= 0) - //{ - // var pos = _columnIndex[col].GetPosition(Row); - // if (pos >= 0) - // { - // var pageItem = _columnIndex[col].Pages[pos]; - // if (pageItem.MinIndex > Row) - // { - // pos--; - // if (pos < 0) - // { - // return default(T); - // } - // else - // { - // pageItem = _columnIndex[col].Pages[pos]; - // } - // } - // short ix = (short)(Row - pageItem.IndexOffset); - // var cellPos = Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, new IndexBase() { Index = ix }); - // if (cellPos >= 0) - // { - // return _values[pageItem.Rows[cellPos].IndexPointer]; - // } - // else //Cell does not exist - // { - // return default(T); - // } - // } - // else //Page does not exist - // { - // return default(T); - // } - //} - //else //Column does not exist - //{ - // return default(T); - //} } private int GetPointer(int row, int column) { @@ -492,125 +400,121 @@ } internal void SetValue(int row, int column, T value) { - lock (_columnIndex) { - var col = - (_columnIndex != null - && column > 0 - && column - 1 < ColumnCount - && column - 1 < _columnIndex.Length - && _columnIndex[column - 1] != null - && _columnIndex[column - 1].Index == column) - ? column - 1 - : Array.BinarySearch( - _columnIndex, - 0, - ColumnCount, - new IndexBase { - Index = (short)(column), - }); - var page = (short)(row >> _pageBits); - if (col >= 0) { - //var pos = Array.BinarySearch(_columnIndex[col].Pages, 0, _columnIndex[col].Count, new IndexBase() { Index = page }); - var pos = _columnIndex[col].GetPosition(row); - if (pos < 0) { - pos = ~pos; - if (pos - 1 < 0 || _columnIndex[col]._pages[pos - 1].IndexOffset + _pageSize - 1 < row) { - AddPage(_columnIndex[col], pos, page); - } else { - pos--; - } - } - if (pos >= _columnIndex[col].PageCount) { + var col = + (_columnIndex != null + && column > 0 + && column - 1 < ColumnCount + && column - 1 < _columnIndex.Length + && _columnIndex[column - 1] != null + && _columnIndex[column - 1].Index == column) + ? column - 1 + : Array.BinarySearch( + _columnIndex, + 0, + ColumnCount, + new IndexBase { + Index = (short)(column), + }); + var page = (short)(row >> _pageBits); + if (col >= 0) { + //var pos = Array.BinarySearch(_columnIndex[col].Pages, 0, _columnIndex[col].Count, new IndexBase() { Index = page }); + var pos = _columnIndex[col].GetPosition(row); + if (pos < 0) { + pos = ~pos; + if (pos - 1 < 0 || _columnIndex[col]._pages[pos - 1].IndexOffset + _pageSize - 1 < row) { AddPage(_columnIndex[col], pos, page); - } - var pageItem = _columnIndex[col]._pages[pos]; - if (pageItem.IndexOffset > row) { - pos--; - page--; - if (pos < 0) { - throw (new("Unexpected error when setting value")); - } - pageItem = _columnIndex[col]._pages[pos]; - } - - short ix = (short)(row - ((pageItem.Index << _pageBits) + pageItem.Offset)); - _searchIx.Index = ix; - var cellPos = - (pageItem.Rows != null - && ix >= 0 - && ix < pageItem.RowCount - && ix < pageItem.Rows.Length - && pageItem.Rows[ix] != null - && pageItem.Rows[ix].Index == ix) - ? ix - : Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchIx); - if (cellPos < 0) { - cellPos = ~cellPos; - AddCell(_columnIndex[col], pos, cellPos, ix, value); } else { - _values[pageItem.Rows[cellPos].IndexPointer] = value; + pos--; } - } else //Column does not exist - { - col = ~col; - AddColumn(col, column); - AddPage(_columnIndex[col], 0, page); - short ix = (short)(row - (page << _pageBits)); - AddCell(_columnIndex[col], 0, 0, ix, value); } + if (pos >= _columnIndex[col].PageCount) { + AddPage(_columnIndex[col], pos, page); + } + var pageItem = _columnIndex[col]._pages[pos]; + if (pageItem.IndexOffset > row) { + pos--; + page--; + if (pos < 0) { + throw (new("Unexpected error when setting value")); + } + pageItem = _columnIndex[col]._pages[pos]; + } + + short ix = (short)(row - ((pageItem.Index << _pageBits) + pageItem.Offset)); + _searchIx.Index = ix; + var cellPos = + (pageItem.Rows != null + && ix >= 0 + && ix < pageItem.RowCount + && ix < pageItem.Rows.Length + && pageItem.Rows[ix] != null + && pageItem.Rows[ix].Index == ix) + ? ix + : Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchIx); + if (cellPos < 0) { + cellPos = ~cellPos; + AddCell(_columnIndex[col], pos, cellPos, ix, value); + } else { + _values[pageItem.Rows[cellPos].IndexPointer] = value; + } + } else //Column does not exist + { + col = ~col; + AddColumn(col, column); + AddPage(_columnIndex[col], 0, page); + short ix = (short)(row - (page << _pageBits)); + AddCell(_columnIndex[col], 0, 0, ix, value); } } internal void Insert(int fromRow, int fromCol, int rows, int columns) { - lock (_columnIndex) { - if (columns > 0) { - var col = GetPosition(fromCol); - if (col < 0) { - col = ~col; - } - for (var c = col; c < ColumnCount; c++) { - _columnIndex[c].Index += (short)columns; - } - } else { - var page = fromRow >> _pageBits; - for (int c = 0; c < ColumnCount; c++) { - var column = _columnIndex[c]; - var pagePos = column.GetPosition(fromRow); - if (pagePos >= 0) { - if (fromRow >= column._pages[pagePos].MinIndex - && fromRow - <= column._pages[pagePos].MaxIndex) //The row is inside the page - { - int offset = fromRow - column._pages[pagePos].IndexOffset; - var rowPos = column._pages[pagePos].GetPosition(offset); - if (rowPos < 0) { - rowPos = ~rowPos; - } - UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows); - } else if (column._pages[pagePos].MinIndex > fromRow - 1 - && pagePos - > 0) //The row is on the page before. - { - int offset = fromRow - ((page - 1) << _pageBits); - var rowPos = column._pages[pagePos - 1].GetPosition(offset); - if (rowPos > 0 && pagePos > 0) { - UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow, rows); - } - } else if (column.PageCount >= pagePos + 1) { - int offset = fromRow - column._pages[pagePos].IndexOffset; - var rowPos = column._pages[pagePos].GetPosition(offset); - if (rowPos < 0) { - rowPos = ~rowPos; - } - if (column._pages[pagePos].RowCount > rowPos) { - UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows); - } else { - UpdateIndexOffset(column, pagePos + 1, 0, fromRow, rows); - } + if (columns > 0) { + var col = GetPosition(fromCol); + if (col < 0) { + col = ~col; + } + for (var c = col; c < ColumnCount; c++) { + _columnIndex[c].Index += (short)columns; + } + } else { + var page = fromRow >> _pageBits; + for (int c = 0; c < ColumnCount; c++) { + var column = _columnIndex[c]; + var pagePos = column.GetPosition(fromRow); + if (pagePos >= 0) { + if (fromRow >= column._pages[pagePos].MinIndex + && fromRow + <= column._pages[pagePos].MaxIndex) //The row is inside the page + { + int offset = fromRow - column._pages[pagePos].IndexOffset; + var rowPos = column._pages[pagePos].GetPosition(offset); + if (rowPos < 0) { + rowPos = ~rowPos; } - } else { - UpdateIndexOffset(column, ~pagePos, 0, fromRow, rows); + UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows); + } else if (column._pages[pagePos].MinIndex > fromRow - 1 + && pagePos + > 0) //The row is on the page before. + { + int offset = fromRow - ((page - 1) << _pageBits); + var rowPos = column._pages[pagePos - 1].GetPosition(offset); + if (rowPos > 0 && pagePos > 0) { + UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow, rows); + } + } else if (column.PageCount >= pagePos + 1) { + int offset = fromRow - column._pages[pagePos].IndexOffset; + var rowPos = column._pages[pagePos].GetPosition(offset); + if (rowPos < 0) { + rowPos = ~rowPos; + } + if (column._pages[pagePos].RowCount > rowPos) { + UpdateIndexOffset(column, pagePos, rowPos, fromRow, rows); + } else { + UpdateIndexOffset(column, pagePos + 1, 0, fromRow, rows); + } } + } else { + UpdateIndexOffset(column, ~pagePos, 0, fromRow, rows); } } } @@ -625,93 +529,87 @@ } internal void Delete(int fromRow, int fromCol, int rows, int columns, bool shift) { - lock (_columnIndex) { - if (columns > 0 && fromRow == 1 && rows >= ExcelPackage.MaxRows) { - DeleteColumns(fromCol, columns, shift); - } else { - var toCol = fromCol + columns - 1; - var pageFromRow = fromRow >> _pageBits; - for (int c = 0; c < ColumnCount; c++) { - var column = _columnIndex[c]; - if (column.Index >= fromCol) { - if (column.Index > toCol) { - break; + if (columns > 0 && fromRow == 1 && rows >= ExcelPackage.MaxRows) { + DeleteColumns(fromCol, columns, shift); + } else { + var toCol = fromCol + columns - 1; + var pageFromRow = fromRow >> _pageBits; + for (int c = 0; c < ColumnCount; c++) { + var column = _columnIndex[c]; + if (column.Index >= fromCol) { + if (column.Index > toCol) { + break; + } + var pagePos = column.GetPosition(fromRow); + if (pagePos < 0) { + pagePos = ~pagePos; + } + if (pagePos < column.PageCount) { + var page = column._pages[pagePos]; + if (shift + && page.RowCount > 0 + && page.MinIndex > fromRow + && page.MaxIndex >= fromRow + rows) { + var o = page.MinIndex - fromRow; + if (o < rows) { + rows -= o; + page.Offset -= o; + UpdatePageOffset(column, pagePos, o); + } else { + page.Offset -= rows; + UpdatePageOffset(column, pagePos, rows); + continue; + } } - var pagePos = column.GetPosition(fromRow); - if (pagePos < 0) { - pagePos = ~pagePos; - } - if (pagePos < column.PageCount) { - var page = column._pages[pagePos]; - if (shift - && page.RowCount > 0 - && page.MinIndex > fromRow - && page.MaxIndex >= fromRow + rows) { - var o = page.MinIndex - fromRow; - if (o < rows) { - rows -= o; - page.Offset -= o; - UpdatePageOffset(column, pagePos, o); - } else { - page.Offset -= rows; - UpdatePageOffset(column, pagePos, rows); - continue; + if (page.RowCount > 0 + && page.MinIndex <= fromRow + rows - 1 + && page.MaxIndex + >= fromRow) //The row is inside the page + { + var endRow = fromRow + rows; + var delEndRow = DeleteCells(column._pages[pagePos], fromRow, endRow, shift); + if (shift && delEndRow != fromRow) { + UpdatePageOffset(column, pagePos, delEndRow - fromRow); + } + if (endRow > delEndRow + && pagePos < column.PageCount + && column._pages[pagePos].MinIndex < endRow) { + pagePos = (delEndRow == fromRow ? pagePos : pagePos + 1); + var rowsLeft = DeletePage( + shift ? fromRow : delEndRow, + endRow - delEndRow, + column, + pagePos, + shift); + //if (shift) UpdatePageOffset(column, pagePos, endRow - fromRow - rowsLeft); + if (rowsLeft > 0) { + var fr = shift ? fromRow : endRow - rowsLeft; + pagePos = column.GetPosition(fr); + DeleteCells(column._pages[pagePos], fr, shift ? fr + rowsLeft : endRow, shift); + if (shift) { + UpdatePageOffset(column, pagePos, rowsLeft); + } } } - if (page.RowCount > 0 - && page.MinIndex <= fromRow + rows - 1 - && page.MaxIndex - >= fromRow) //The row is inside the page - { - var endRow = fromRow + rows; - var delEndRow = DeleteCells(column._pages[pagePos], fromRow, endRow, shift); - if (shift && delEndRow != fromRow) { - UpdatePageOffset(column, pagePos, delEndRow - fromRow); + } else if (pagePos > 0 + && column._pages[pagePos].IndexOffset + > fromRow) //The row is on the page before. + { + int offset = fromRow + rows - 1 - ((pageFromRow - 1) << _pageBits); + var rowPos = column._pages[pagePos - 1].GetPosition(offset); + if (rowPos > 0 && pagePos > 0) { + if (shift) { + UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow + rows - 1, -rows); } - if (endRow > delEndRow - && pagePos < column.PageCount - && column._pages[pagePos].MinIndex < endRow) { - pagePos = (delEndRow == fromRow ? pagePos : pagePos + 1); - var rowsLeft = DeletePage( - shift ? fromRow : delEndRow, - endRow - delEndRow, - column, - pagePos, - shift); - //if (shift) UpdatePageOffset(column, pagePos, endRow - fromRow - rowsLeft); - if (rowsLeft > 0) { - var fr = shift ? fromRow : endRow - rowsLeft; - pagePos = column.GetPosition(fr); - delEndRow = DeleteCells( - column._pages[pagePos], - fr, - shift ? fr + rowsLeft : endRow, - shift); - if (shift) { - UpdatePageOffset(column, pagePos, rowsLeft); - } - } - } - } else if (pagePos > 0 - && column._pages[pagePos].IndexOffset - > fromRow) //The row is on the page before. - { - int offset = fromRow + rows - 1 - ((pageFromRow - 1) << _pageBits); - var rowPos = column._pages[pagePos - 1].GetPosition(offset); - if (rowPos > 0 && pagePos > 0) { - if (shift) { - UpdateIndexOffset(column, pagePos - 1, rowPos, fromRow + rows - 1, -rows); - } - } - } else { - if (shift && pagePos + 1 < column.PageCount) { - UpdateIndexOffset( - column, - pagePos + 1, - 0, - column._pages[pagePos + 1].MinIndex, - -rows); - } + } + } else { + if (shift && pagePos + 1 < column.PageCount) { + UpdateIndexOffset( + column, + pagePos + 1, + 0, + column._pages[pagePos + 1].MinIndex, + -rows); } } } @@ -737,31 +635,12 @@ || Math.Abs(column._pages[pagePos].Rows[column._pages[pagePos].RowCount - 1].Index) > _pageSizeMax) //Split or Merge??? { - rows = ResetPageOffset(column, pagePos, rows); - ////MergePages - //if (column.Pages[pagePos - 1].Index + 1 == column.Pages[pagePos].Index) - //{ - // if (column.Pages[pagePos].IndexOffset + column.Pages[pagePos].Rows[column.Pages[pagePos].RowCount - 1].Index + rows - - // column.Pages[pagePos - 1].IndexOffset + column.Pages[pagePos - 1].Rows[0].Index <= PageSize) - // { - // //Merge - // MergePage(column, pagePos - 1, -rows); - // } - // else - // { - // //Split - // } - //} - //rows -= PageSize; - //for (int p = pagePos; p < column.PageCount; p++) - //{ - // column.Pages[p].Index -= 1; - //} + ResetPageOffset(column, pagePos, rows); } } } - private int ResetPageOffset(ColumnIndex column, int pagePos, int rows) { + private void ResetPageOffset(ColumnIndex column, int pagePos, int rows) { PageIndex fromPage = column._pages[pagePos]; PageIndex toPage; short pageAdd = 0; @@ -775,14 +654,6 @@ + toPage.Rows[0].Index <= _pageSizeMax) { MergePage(column, pagePos - 1); - //var newPage = new PageIndex(toPage, 0, GetSize(fromPage.RowCount + toPage.RowCount)); - //newPage.RowCount = fromPage.RowCount + fromPage.RowCount; - //Array.Copy(toPage.Rows, 0, newPage.Rows, 0, toPage.RowCount); - //Array.Copy(fromPage.Rows, 0, newPage.Rows, toPage.RowCount, fromPage.RowCount); - //for (int r = toPage.RowCount; r < newPage.RowCount; r++) - //{ - // newPage.Rows[r].Index += (short)(fromPage.IndexOffset - toPage.IndexOffset); - //} } } else //No page after { @@ -797,7 +668,6 @@ fromPage.Offset += _pageSize; } } - return rows; } private int DeletePage(int fromRow, int rows, ColumnIndex column, int pagePos, bool shift) { @@ -841,7 +711,6 @@ return rows; } - /// private int DeleteCells(PageIndex page, int fromRow, int toRow, bool shift) { var fromPos = page.GetPosition(fromRow - (page.IndexOffset)); if (fromPos < 0) { @@ -908,13 +777,10 @@ } if (_columnIndex[fPos].Index >= fromCol && _columnIndex[fPos].Index <= fromCol + columns) { - //if (_columnIndex[fPos].Index < ColumnCount) - //{ if (tPos < ColumnCount) { Array.Copy(_columnIndex, tPos, _columnIndex, fPos, ColumnCount - tPos); } ColumnCount -= (tPos - fPos); - //} } if (shift) { for (var c = fPos; c < ColumnCount; c++) { @@ -1243,22 +1109,6 @@ ColumnCount++; } - private int _colPos = -1, - _row; - - public ulong Current => ((ulong)_row << 32) | (uint)(_columnIndex[_colPos].Index); - - //object IEnumerator.Current - //{ - // get - // { - // return GetValue(_row+1, _columnIndex[_colPos].Index); - // } - //} - public bool MoveNext() { - return GetNextCell(ref _row, ref _colPos, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); - } - internal bool NextCell(ref int row, ref int col) { return NextCell(ref row, ref col, 0, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); } @@ -1379,47 +1229,11 @@ return GetNextCell(ref row, ref colPos, startColPos, endRow, endColPos); } - internal bool GetNextCell( - ref int row, - ref int colPos, - int startColPos, - int endRow, - int endColPos, - ref int[] pagePos, - ref int[] cellPos) { - if (colPos == endColPos) { - colPos = startColPos; - row++; - } else { - colPos++; - } - - if (pagePos[colPos] < 0) { - if (pagePos[colPos] == -1) { - pagePos[colPos] = _columnIndex[colPos].GetPosition(row); - } - } else if (_columnIndex[colPos]._pages[pagePos[colPos]].RowCount <= row) { - if (_columnIndex[colPos].PageCount > pagePos[colPos]) { - pagePos[colPos]++; - } else { - pagePos[colPos] = -2; - } - } - - var r = - _columnIndex[colPos]._pages[pagePos[colPos]].IndexOffset - + _columnIndex[colPos]._pages[pagePos[colPos]].Rows[cellPos[colPos]].Index; - if (r == row) { - row = r; - } - return true; - } - internal bool PrevCell(ref int row, ref int col) { return PrevCell(ref row, ref col, 0, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); } - internal bool PrevCell( + private bool PrevCell( ref int row, ref int col, int minRow, @@ -1542,28 +1356,10 @@ colPos = ColumnCount; row--; if (row < startRow) { - Reset(); return false; } return GetPrevCell(ref colPos, ref row, startRow, startColPos, endColPos); } - - public void Reset() { - _colPos = -1; - _row = 0; - } - - //public IEnumerator<ulong> GetEnumerator() - //{ - // this.Reset(); - // return this; - //} - - //IEnumerator IEnumerable.GetEnumerator() - //{ - // this.Reset(); - // return this; - //} } internal class CellsStoreEnumerator<T> : IEnumerable<T>, IEnumerator<T> { @@ -1639,29 +1435,14 @@ } internal T Value { - get { - lock (_cellStore) { - return _cellStore.GetValue(row, Column); - } - } - set { - lock (_cellStore) { - _cellStore.SetValue(row, Column, value); - } - } + get => _cellStore.GetValue(row, Column); + set => _cellStore.SetValue(row, Column, value); } internal bool Next() { - //return _cellStore.GetNextCell(ref row, ref colPos, minColPos, maxRow, maxColPos); return _cellStore.GetNextCell(ref row, ref colPos, minColPos, maxRow, maxColPos); } - internal bool Previous() { - lock (_cellStore) { - return _cellStore.GetPrevCell(ref row, ref colPos, minRow, minColPos, maxColPos); - } - } - public string CellAddress => ExcelCellBase.GetAddress(Row, Column); public IEnumerator<T> GetEnumerator() {
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs index 08605aa..8717d2e 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingCollection.cs
@@ -31,6 +31,7 @@ using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts; using OfficeOpenXml.Utils; @@ -68,6 +69,9 @@ private readonly List<IExcelConditionalFormattingRule> _rules = new(); private readonly ExcelWorksheet _worksheet; + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorksheet.WorksheetSchemaNodeOrder; + /// <summary> /// Initialize the <see cref="ExcelConditionalFormattingCollection"/> /// </summary> @@ -77,7 +81,6 @@ Require.Argument(worksheet).IsNotNull("worksheet"); _worksheet = worksheet; - SchemaNodeOrder = _worksheet.SchemaNodeOrder; // Look for all the <conditionalFormatting> var conditionalFormattingNodes = TopNode.SelectNodes(
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs index 84d8fb1..59376e7 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingColorScaleValue.cs
@@ -30,6 +30,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.Utils; @@ -44,6 +45,11 @@ private eExcelConditionalFormattingRuleType _ruleType; private readonly ExcelWorksheet _worksheet; + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + ExcelConditionalFormattingConstants.Nodes._cfvo, + ExcelConditionalFormattingConstants.Nodes._color, + ]; + /// <summary> /// Initialize the cfvo (§18.3.1.11) node /// </summary> @@ -78,12 +84,6 @@ // Save the worksheet for private methods to use _worksheet = worksheet; - // Schema order list - SchemaNodeOrder = new[] { - ExcelConditionalFormattingConstants.Nodes._cfvo, - ExcelConditionalFormattingConstants.Nodes._color, - }; - // Check if the parent does not exists if (itemElementNode == null) { // Get the parent node path by the rule type
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs index bffef35..c38d095 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingIconDatabarValue.cs
@@ -30,6 +30,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Drawing; using System.Globalization; using System.Xml; @@ -117,6 +118,10 @@ Formula = formula; } + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + ExcelConditionalFormattingConstants.Nodes._cfvo, + ]; + /// <summary> /// Initialize the cfvo (§18.3.1.11) node /// </summary> @@ -139,9 +144,6 @@ // Save the worksheet for private methods to use _worksheet = worksheet; - // Schema order list - SchemaNodeOrder = new[] { ExcelConditionalFormattingConstants.Nodes._cfvo }; - //Check if the parent does not exists if (itemElementNode == null) { // Get the parent node path by the rule type
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs index cf0fae8..c513d8d 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingOperatorType.cs
@@ -29,8 +29,6 @@ * Eyal Seagull Conditional Formatting Adaption 2012-04-17 *******************************************************************************/ -using System; - namespace OfficeOpenXml.ConditionalFormatting; /// <summary>
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs index b1c6b32..8168caa 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingRuleType.cs
@@ -29,7 +29,6 @@ * Eyal Seagull Conditional Formatting Adaption 2012-04-03 *******************************************************************************/ -using System; using System.Xml; namespace OfficeOpenXml.ConditionalFormatting;
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs index 2051e7b..c93a82d 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingTimePeriodType.cs
@@ -29,8 +29,6 @@ * Eyal Seagull Conditional Formatting Adaption 2012-04-17 *******************************************************************************/ -using System; - namespace OfficeOpenXml.ConditionalFormatting; /// <summary>
diff --git a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs index 9d28645..22d9356 100644 --- a/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs +++ b/EPPlus/ConditionalFormatting/ExcelConditionalFormattingValueObjectType.cs
@@ -29,7 +29,6 @@ * Eyal Seagull Conditional Formatting Adaption 2012-04-03 *******************************************************************************/ -using System; using System.Xml; namespace OfficeOpenXml.ConditionalFormatting;
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs index 8ca4be2..6c37773 100644 --- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs +++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingDataBar.cs
@@ -29,7 +29,7 @@ * Eyal Seagull Added 2012-04-03 *******************************************************************************/ -using System.Globalization; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts; @@ -41,6 +41,8 @@ public class ExcelConditionalFormattingDataBar : ExcelConditionalFormattingRule, IExcelConditionalFormattingDataBarGroup { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = ["cfvo", "color"]; + /// <summary> /// /// </summary> @@ -64,7 +66,6 @@ worksheet, itemElementNode, namespaceManager ?? worksheet.NameSpaceManager) { - SchemaNodeOrder = new[] { "cfvo", "color" }; //Create the <dataBar> node inside the <cfRule> node if (itemElementNode != null && itemElementNode.HasChildNodes) { bool high = false;
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs index a85ebaa..082da63 100644 --- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs +++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingRule.cs
@@ -30,6 +30,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts; using OfficeOpenXml.Style.Dxf; @@ -50,6 +51,9 @@ /// </summary> private static bool _changingPriority; + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorksheet.WorksheetSchemaNodeOrder; + /// <summary> /// Initialize the <see cref="ExcelConditionalFormattingRule"/> /// </summary> @@ -73,7 +77,6 @@ _type = type; _worksheet = worksheet; - SchemaNodeOrder = _worksheet.SchemaNodeOrder; if (itemElementNode == null) { // Create/Get the <cfRule> inside <conditionalFormatting>
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs index 5700e10..23c8d02 100644 --- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs +++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingThreeColorScale.cs
@@ -29,7 +29,6 @@ * Eyal Seagull Added 2012-04-03 *******************************************************************************/ -using System.Drawing; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts;
diff --git a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs index ad7d79f..691f0dd 100644 --- a/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs +++ b/EPPlus/ConditionalFormatting/Rules/ExcelConditionalFormattingTwoColorScale.cs
@@ -29,7 +29,6 @@ * Eyal Seagull Added 2012-04-03 *******************************************************************************/ -using System.Drawing; using System.Xml; using OfficeOpenXml.ConditionalFormatting.Contracts;
diff --git a/EPPlus/DataValidation/ExcelDataValidation.cs b/EPPlus/DataValidation/ExcelDataValidation.cs index 88a60de..1e5f445 100644 --- a/EPPlus/DataValidation/ExcelDataValidation.cs +++ b/EPPlus/DataValidation/ExcelDataValidation.cs
@@ -31,6 +31,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Globalization; using System.Text.RegularExpressions; using System.Xml; @@ -59,6 +60,22 @@ protected readonly string _formula1Path = "d:formula1"; protected readonly string _formula2Path = "d:formula2"; + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "type", + "errorStyle", + "operator", + "allowBlank", + "showInputMessage", + "showErrorMessage", + "errorTitle", + "error", + "promptTitle", + "prompt", + "sqref", + "formula1", + "formula2", + ]; + internal ExcelDataValidation( ExcelWorksheet worksheet, string address, @@ -112,26 +129,6 @@ TopNode = itemElementNode; ValidationType = validationType; Address = new(address); - Init(); - } - - private void Init() { - // set schema node order - SchemaNodeOrder = new[] { - "type", - "errorStyle", - "operator", - "allowBlank", - "showInputMessage", - "showErrorMessage", - "errorTitle", - "error", - "promptTitle", - "prompt", - "sqref", - "formula1", - "formula2", - }; } private string CheckAndFixRangeAddress(string address) {
diff --git a/EPPlus/DataValidation/ExcelDataValidationCollection.cs b/EPPlus/DataValidation/ExcelDataValidationCollection.cs index 4518881..3c186a5 100644 --- a/EPPlus/DataValidation/ExcelDataValidationCollection.cs +++ b/EPPlus/DataValidation/ExcelDataValidationCollection.cs
@@ -36,6 +36,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.DataValidation.Contracts; using OfficeOpenXml.Utils; @@ -75,6 +76,9 @@ "{0}/d:dataValidation", _dataValidationPath); + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorksheet.WorksheetSchemaNodeOrder; + /// <summary> /// Constructor /// </summary> @@ -83,7 +87,6 @@ : base(worksheet.NameSpaceManager, worksheet.WorksheetXml.DocumentElement) { Require.Argument(worksheet).IsNotNull("worksheet"); _worksheet = worksheet; - SchemaNodeOrder = worksheet.SchemaNodeOrder; // check existing nodes and load them var dataValidationNodes = worksheet.WorksheetXml.SelectNodes(
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs index df934c8..ade7974 100644 --- a/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs +++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingBase.cs
@@ -30,6 +30,7 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ +using System.Collections.Immutable; using System.Xml; namespace OfficeOpenXml.Drawing.Vml; @@ -70,28 +71,28 @@ /// Drawing object used for comments /// </summary> public class ExcelVmlDrawingBase : XmlHelper { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "fill", + "stroke", + "shadow", + "path", + "textbox", + "ClientData", + "MoveWithCells", + "SizeWithCells", + "Anchor", + "Locked", + "AutoFill", + "LockText", + "TextHAlign", + "TextVAlign", + "Row", + "Column", + "Visible", + ]; + internal ExcelVmlDrawingBase(XmlNode topNode, XmlNamespaceManager ns) - : base(ns, topNode) { - SchemaNodeOrder = new[] { - "fill", - "stroke", - "shadow", - "path", - "textbox", - "ClientData", - "MoveWithCells", - "SizeWithCells", - "Anchor", - "Locked", - "AutoFill", - "LockText", - "TextHAlign", - "TextVAlign", - "Row", - "Column", - "Visible", - }; - } + : base(ns, topNode) {} public string Id { get => GetXmlNodeString("@id");
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs index 912214e..a0bd7e1 100644 --- a/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs +++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingBaseCollection.cs
@@ -38,7 +38,6 @@ public class ExcelVmlDrawingBaseCollection { internal ExcelVmlDrawingBaseCollection(ExcelPackage pck, ExcelWorksheet ws, Uri uri) { - VmlDrawingXml = new(); VmlDrawingXml.PreserveWhitespace = false; NameTable nt = new NameTable(); @@ -55,7 +54,8 @@ } } - internal XmlDocument VmlDrawingXml { get; set; } + internal XmlDocument VmlDrawingXml { get; set; } = new(); + internal Uri Uri { get; set; } internal string RelId { get; set; } internal ZipPackagePart Part { get; set; }
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs index bd137cd..7eecb26 100644 --- a/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs +++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingComment.cs
@@ -30,6 +30,7 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ +using System.Collections.Immutable; using System.Xml; namespace OfficeOpenXml.Drawing.Vml; @@ -38,28 +39,29 @@ /// Drawing object used for comments /// </summary> public class ExcelVmlDrawingComment : ExcelVmlDrawingBase, IRangeId { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "fill", + "stroke", + "shadow", + "path", + "textbox", + "ClientData", + "MoveWithCells", + "SizeWithCells", + "Anchor", + "Locked", + "AutoFill", + "LockText", + "TextHAlign", + "TextVAlign", + "Row", + "Column", + "Visible", + ]; + internal ExcelVmlDrawingComment(XmlNode topNode, ExcelRangeBase range, XmlNamespaceManager ns) : base(topNode, ns) { Range = range; - SchemaNodeOrder = new[] { - "fill", - "stroke", - "shadow", - "path", - "textbox", - "ClientData", - "MoveWithCells", - "SizeWithCells", - "Anchor", - "Locked", - "AutoFill", - "LockText", - "TextHAlign", - "TextVAlign", - "Row", - "Column", - "Visible", - }; } internal ExcelRangeBase Range { get; set; }
diff --git a/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs b/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs index 294f9b2..ec28659 100644 --- a/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs +++ b/EPPlus/Drawing/Vml/ExcelVmlDrawingPosition.cs
@@ -30,7 +30,6 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ -using System; using System.Xml; namespace OfficeOpenXml.Drawing.Vml;
diff --git a/EPPlus/EPPlusSDK.csproj b/EPPlus/EPPlusSDK.csproj index 2cdc079..2a2e866 100644 --- a/EPPlus/EPPlusSDK.csproj +++ b/EPPlus/EPPlusSDK.csproj
@@ -4,6 +4,6 @@ <RootNamespace>OfficeOpenXml</RootNamespace> <AssemblyName>EPPlus</AssemblyName> <PackageId>Appsheet.EPPlus</PackageId> - <Version>1.0.5</Version> + <Version>1.0.6</Version> </PropertyGroup> </Project>
diff --git a/EPPlus/ExcelAddress.cs b/EPPlus/ExcelAddress.cs index 9a5f20d..51ad999 100644 --- a/EPPlus/ExcelAddress.cs +++ b/EPPlus/ExcelAddress.cs
@@ -33,7 +33,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text.RegularExpressions; namespace OfficeOpenXml; @@ -837,7 +836,6 @@ } internal static AddressType IsValid(string address) { - double d; if (address == "#REF!") { return AddressType.Invalid; } @@ -845,15 +843,14 @@ address, NumberStyles.Any, CultureInfo.InvariantCulture, - out d)) //A double, no valid address + out _)) //A double, no valid address { return AddressType.Invalid; } if (IsFormula(address)) { return AddressType.Formula; } - string ws; - if (SplitAddress(address, out var wb, out ws, out var intAddress)) { + if (SplitAddress(address, out var wb, out _, out var intAddress)) { if (intAddress.Contains( "[")) //Table reference { @@ -868,72 +865,6 @@ return string.IsNullOrEmpty(wb) ? AddressType.InternalName : AddressType.ExternalName; } return AddressType.Invalid; - - //if(string.IsNullOrEmpty(wb)); - //ExcelAddress a = new ExcelAddress(Address); - //if (Address.IndexOf('!') > 0) - //{ - // string[] split = Address.Split('!'); - // if (split.Length == 2) - // { - // ws = split[0]; - // Address = split[1]; - // } - // else if (split.Length == 3 && split[1] == "#REF" && split[2] == "") - // { - // ws = split[0]; - // Address = "#REF!"; - // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) - // { - // return AddressType.ExternalAddress; - // } - // else - // { - // return AddressType.InternalAddress; - // } - // } - // else - // { - // return AddressType.Invalid; - // } - //} - //int _fromRow, column, _toRow, _toCol; - //if (ExcelAddressBase.GetRowColFromAddress(Address, out _fromRow, out column, out _toRow, out _toCol)) - //{ - // if (_fromRow > 0 && column > 0 && _toRow <= ExcelPackage.MaxRows && _toCol <= ExcelPackage.MaxColumns) - // { - // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) - // { - // return AddressType.ExternalAddress; - // } - // else - // { - // return AddressType.InternalAddress; - // } - // } - // else - // { - // return AddressType.Invalid; - // } - //} - //else - //{ - // if(IsValidName(Address)) - // { - // if (ws.StartsWith("[") && ws.IndexOf("]") > 1) - // { - // return AddressType.ExternalName; - // } - // else - // { - // return AddressType.InternalName; - // } - // } - // else - // { - // return AddressType.Invalid; - // } - //} } private static bool IsAddress(string intAddress) { @@ -1040,12 +971,8 @@ public int Columns => _toCol - _fromCol + 1; - internal bool IsMultiCell() { - return (_fromRow < _fromCol || _fromCol < _toCol); - } - internal static String GetWorkbookPart(string address) { - var ix = 0; + int ix; if (address[0] == '[') { ix = address.IndexOf(']') + 1; if (ix > 0) { @@ -1055,11 +982,6 @@ return ""; } - internal static string GetWorksheetPart(string address, string defaultWorkSheet) { - int ix = 0; - return GetWorksheetPart(address, defaultWorkSheet, ref ix); - } - internal static string GetWorksheetPart(string address, string defaultWorkSheet, ref int endIx) { if (address == "") { return defaultWorkSheet; @@ -1081,18 +1003,6 @@ return defaultWorkSheet; } - internal static string GetAddressPart(string address) { - var ix = 0; - GetWorksheetPart(address, "", ref ix); - if (ix < address.Length) { - if (address[ix] == '!') { - return address.Substring(ix + 1); - } - return ""; - } - return ""; - } - internal static void SplitAddress( string fullAddress, out string wb, @@ -1115,9 +1025,7 @@ private static string GetString(string address, int ix, out int endIx) { var strIx = address.IndexOf("''"); - var prevStrIx = ix; while (strIx > -1) { - prevStrIx = strIx; strIx = address.IndexOf("''"); } endIx = address.IndexOf("'"); @@ -1189,8 +1097,6 @@ _fromColFixed, _toColFixed; - internal ExcelFormulaAddress() {} - public ExcelFormulaAddress(int fromRow, int fromCol, int toRow, int toColumn) : base(fromRow, fromCol, toRow, toColumn) { _ws = ""; @@ -1201,24 +1107,6 @@ SetFixed(); } - internal ExcelFormulaAddress(string ws, string address) - : base(address) { - if (string.IsNullOrEmpty(_ws)) { - _ws = ws; - } - SetFixed(); - } - - internal ExcelFormulaAddress(string ws, string address, bool isName) - : base(address, isName) { - if (string.IsNullOrEmpty(_ws)) { - _ws = ws; - } - if (!isName) { - SetFixed(); - } - } - private void SetFixed() { if (Address.IndexOf("[") >= 0) { return;
diff --git a/EPPlus/ExcelCellBase.cs b/EPPlus/ExcelCellBase.cs index df48cb3..5a0b25e 100644 --- a/EPPlus/ExcelCellBase.cs +++ b/EPPlus/ExcelCellBase.cs
@@ -153,7 +153,6 @@ if (isText == false && part != "" && prevTq == c) { ret += addressTranslator(part, row, col, rowIncr, colIncr); part = ""; - prevTq = (char)0; } prevTq = c; isText = !isText; @@ -220,7 +219,6 @@ if (isText == false && part != "" && prevTq == c) { ret += addressTranslator(part, row, col, rowIncr, colIncr); part = ""; - prevTq = (char)0; } prevTq = c; isText = !isText;
diff --git a/EPPlus/ExcelColumn.cs b/EPPlus/ExcelColumn.cs index 31bd864..889c682 100644 --- a/EPPlus/ExcelColumn.cs +++ b/EPPlus/ExcelColumn.cs
@@ -30,7 +30,6 @@ * Jan K�llman License changed GPL-->LGPL 2011-12-27 *******************************************************************************/ -using System; using System.Xml; using OfficeOpenXml.Style;
diff --git a/EPPlus/ExcelComment.cs b/EPPlus/ExcelComment.cs index 3930bde..74df866 100644 --- a/EPPlus/ExcelComment.cs +++ b/EPPlus/ExcelComment.cs
@@ -40,7 +40,7 @@ /// An Excel Cell Comment /// </summary> public class ExcelComment : ExcelVmlDrawingComment { - internal XmlHelper _commentHelper; + private readonly XmlHelper _commentHelper; private readonly string _text; internal ExcelComment(XmlNamespaceManager ns, XmlNode commentTopNode, ExcelRangeBase cell) @@ -83,10 +83,6 @@ _commentHelper.NameSpaceManager) .InnerText; } - set { - int authorRef = GetAuthor(value); - _commentHelper.SetXmlNodeString("@authorId", authorRef.ToString()); - } } private int GetAuthor(string value) { @@ -141,5 +137,5 @@ /// <summary> /// Richtext collection /// </summary> - public ExcelRichTextCollection RichText { get; set; } + internal ExcelRichTextCollection RichText { get; set; } }
diff --git a/EPPlus/ExcelCommentCollection.cs b/EPPlus/ExcelCommentCollection.cs index 9146df7..70a4820 100644 --- a/EPPlus/ExcelCommentCollection.cs +++ b/EPPlus/ExcelCommentCollection.cs
@@ -45,7 +45,6 @@ internal RangeCollection _comments; internal ExcelCommentCollection(ExcelPackage pck, ExcelWorksheet ws, XmlNamespaceManager ns) { - CommentXml = new(); CommentXml.PreserveWhitespace = false; NameSpaceManager = ns; Worksheet = ws; @@ -89,7 +88,7 @@ /// <summary> /// Access to the comment xml document /// </summary> - public XmlDocument CommentXml { get; set; } + public XmlDocument CommentXml { get; set; } = new(); internal Uri Uri { get; set; }
diff --git a/EPPlus/ExcelHeaderFooter.cs b/EPPlus/ExcelHeaderFooter.cs deleted file mode 100644 index c4a3eeb..0000000 --- a/EPPlus/ExcelHeaderFooter.cs +++ /dev/null
@@ -1,349 +0,0 @@ -/******************************************************************************* - * You may amend and distribute as you like, but don't remove this header! - * - * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. - * See http://www.codeplex.com/EPPlus for details. - * - * Copyright (C) 2011 Jan Källman - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php - * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html - * - * All code and executables are provided "as is" with no warranty either express or implied. - * The author accepts no liability for any damage or loss of business that this product may cause. - * - * Code change notes: - * - * Author Change Date - ******************************************************************************* - * Jan Källman Initial Release 2009-10-01 - * Jan Källman Total rewrite 2010-03-01 - * Jan Källman License changed GPL-->LGPL 2011-12-27 - * *******************************************************************************/ - -using System.Xml; - -namespace OfficeOpenXml; - -/// <summary> -/// Print header and footer -/// </summary> -public class ExcelHeaderFooterText { - private ExcelWorksheet _ws; - private string _hf; - - internal ExcelHeaderFooterText(XmlNode textNode, ExcelWorksheet ws, string hf) { - _ws = ws; - _hf = hf; - if (textNode == null || string.IsNullOrEmpty(textNode.InnerText)) { - return; - } - string text = textNode.InnerText; - string code = text.Substring(0, 2); - int startPos = 2; - for (int pos = startPos; pos < text.Length - 2; pos++) { - string newCode = text.Substring(pos, 2); - if (newCode == "&C" || newCode == "&R") { - SetText(code, text.Substring(startPos, pos - startPos)); - startPos = pos + 2; - pos = startPos; - code = newCode; - } - } - SetText(code, text.Substring(startPos, text.Length - startPos)); - } - - private void SetText(string code, string text) { - switch (code) { - case "&L": - LeftAlignedText = text; - break; - case "&C": - CenteredText = text; - break; - default: - RightAlignedText = text; - break; - } - } - - /// <summary> - /// Get/set the text to appear on the left hand side of the header (or footer) on the worksheet. - /// </summary> - public string LeftAlignedText; - - /// <summary> - /// Get/set the text to appear in the center of the header (or footer) on the worksheet. - /// </summary> - public string CenteredText; - - /// <summary> - /// Get/set the text to appear on the right hand side of the header (or footer) on the worksheet. - /// </summary> - public string RightAlignedText; -} - -/// <summary> -/// Represents the Header and Footer on an Excel Worksheet -/// </summary> -public sealed class ExcelHeaderFooter : XmlHelper { - /// <summary> - /// The code for "current page #" - /// </summary> - public const string PageNumber = "&P"; - - /// <summary> - /// The code for "total pages" - /// </summary> - public const string NumberOfPages = "&N"; - - /// <summary> - /// The code for "text font color" - /// RGB Color is specified as RRGGBB - /// Theme Color is specified as TTSNN where TT is the theme color Id, S is either "+" or "-" of the tint/shade value, NN is the tint/shade value. - /// </summary> - public const string FontColor = "&K"; - - /// <summary> - /// The code for "sheet tab name" - /// </summary> - public const string SheetName = "&A"; - - /// <summary> - /// The code for "this workbook's file path" - /// </summary> - public const string FilePath = "&Z"; - - /// <summary> - /// The code for "this workbook's file name" - /// </summary> - public const string FileName = "&F"; - - /// <summary> - /// The code for "date" - /// </summary> - public const string CurrentDate = "&D"; - - /// <summary> - /// The code for "time" - /// </summary> - public const string CurrentTime = "&T"; - - /// <summary> - /// The code for "picture as background" - /// </summary> - public const string Image = "&G"; - - /// <summary> - /// The code for "outline style" - /// </summary> - public const string OutlineStyle = "&O"; - - /// <summary> - /// The code for "shadow style" - /// </summary> - public const string ShadowStyle = "&H"; - - internal ExcelHeaderFooterText _oddHeader; - internal ExcelHeaderFooterText _oddFooter; - internal ExcelHeaderFooterText _evenHeader; - internal ExcelHeaderFooterText _evenFooter; - internal ExcelHeaderFooterText _firstHeader; - internal ExcelHeaderFooterText _firstFooter; - private readonly ExcelWorksheet _ws; - - /// <summary> - /// ExcelHeaderFooter Constructor - /// </summary> - /// <param name="nameSpaceManager"></param> - /// <param name="topNode"></param> - /// <param name="ws">The worksheet</param> - internal ExcelHeaderFooter( - XmlNamespaceManager nameSpaceManager, - XmlNode topNode, - ExcelWorksheet ws) - : base(nameSpaceManager, topNode) { - _ws = ws; - SchemaNodeOrder = new[] { - "headerFooter", - "oddHeader", - "oddFooter", - "evenHeader", - "evenFooter", - "firstHeader", - "firstFooter", - }; - } - - private const string _alignWithMarginsPath = "@alignWithMargins"; - - /// <summary> - /// Gets/sets the alignWithMargins attribute - /// </summary> - public bool AlignWithMargins { - get => GetXmlNodeBool(_alignWithMarginsPath); - set => SetXmlNodeString(_alignWithMarginsPath, value ? "1" : "0"); - } - - private const string _differentOddEvenPath = "@differentOddEven"; - - /// <summary> - /// Gets/sets the flag that tells Excel to display different headers and footers on odd and even pages. - /// </summary> - public bool differentOddEven { - get => GetXmlNodeBool(_differentOddEvenPath); - set => SetXmlNodeString(_differentOddEvenPath, value ? "1" : "0"); - } - - private const string _differentFirstPath = "@differentFirst"; - - /// <summary> - /// Gets/sets the flag that tells Excel to display different headers and footers on the first page of the worksheet. - /// </summary> - public bool differentFirst { - get => GetXmlNodeBool(_differentFirstPath); - set => SetXmlNodeString(_differentFirstPath, value ? "1" : "0"); - } - - /// <summary> - /// Provides access to the header on odd numbered pages of the document. - /// If you want the same header on both odd and even pages, then only set values in this ExcelHeaderFooterText class. - /// </summary> - public ExcelHeaderFooterText OddHeader { - get { - if (_oddHeader == null) { - _oddHeader = new(TopNode.SelectSingleNode("d:oddHeader", NameSpaceManager), _ws, "H"); - } - return _oddHeader; - } - } - - /// <summary> - /// Provides access to the footer on odd numbered pages of the document. - /// If you want the same footer on both odd and even pages, then only set values in this ExcelHeaderFooterText class. - /// </summary> - public ExcelHeaderFooterText OddFooter { - get { - if (_oddFooter == null) { - _oddFooter = new(TopNode.SelectSingleNode("d:oddFooter", NameSpaceManager), _ws, "F"); - ; - } - return _oddFooter; - } - } - - // evenHeader and evenFooter set differentOddEven = true - /// <summary> - /// Provides access to the header on even numbered pages of the document. - /// </summary> - public ExcelHeaderFooterText EvenHeader { - get { - if (_evenHeader == null) { - _evenHeader = new(TopNode.SelectSingleNode("d:evenHeader", NameSpaceManager), _ws, "HEVEN"); - differentOddEven = true; - } - return _evenHeader; - } - } - - /// <summary> - /// Provides access to the footer on even numbered pages of the document. - /// </summary> - public ExcelHeaderFooterText EvenFooter { - get { - if (_evenFooter == null) { - _evenFooter = new(TopNode.SelectSingleNode("d:evenFooter", NameSpaceManager), _ws, "FEVEN"); - differentOddEven = true; - } - return _evenFooter; - } - } - - /// <summary> - /// Provides access to the header on the first page of the document. - /// </summary> - public ExcelHeaderFooterText FirstHeader { - get { - if (_firstHeader == null) { - _firstHeader = new( - TopNode.SelectSingleNode("d:firstHeader", NameSpaceManager), - _ws, - "HFIRST"); - differentFirst = true; - } - return _firstHeader; - } - } - - /// <summary> - /// Provides access to the footer on the first page of the document. - /// </summary> - public ExcelHeaderFooterText FirstFooter { - get { - if (_firstFooter == null) { - _firstFooter = new( - TopNode.SelectSingleNode("d:firstFooter", NameSpaceManager), - _ws, - "FFIRST"); - differentFirst = true; - } - return _firstFooter; - } - } - - /// <summary> - /// Saves the header and footer information to the worksheet XML - /// </summary> - internal void Save() { - if (_oddHeader != null) { - SetXmlNodeString("d:oddHeader", GetText(OddHeader)); - } - if (_oddFooter != null) { - SetXmlNodeString("d:oddFooter", GetText(OddFooter)); - } - - // only set evenHeader and evenFooter - if (differentOddEven) { - if (_evenHeader != null) { - SetXmlNodeString("d:evenHeader", GetText(EvenHeader)); - } - if (_evenFooter != null) { - SetXmlNodeString("d:evenFooter", GetText(EvenFooter)); - } - } - - // only set firstHeader and firstFooter - if (differentFirst) { - if (_firstHeader != null) { - SetXmlNodeString("d:firstHeader", GetText(FirstHeader)); - } - if (_firstFooter != null) { - SetXmlNodeString("d:firstFooter", GetText(FirstFooter)); - } - } - } - - private string GetText(ExcelHeaderFooterText headerFooter) { - string ret = ""; - if (headerFooter.LeftAlignedText != null) { - ret += "&L" + headerFooter.LeftAlignedText; - } - if (headerFooter.CenteredText != null) { - ret += "&C" + headerFooter.CenteredText; - } - if (headerFooter.RightAlignedText != null) { - ret += "&R" + headerFooter.RightAlignedText; - } - return ret; - } -}
diff --git a/EPPlus/ExcelPackage.cs b/EPPlus/ExcelPackage.cs index be8f56e..7d12a0e 100644 --- a/EPPlus/ExcelPackage.cs +++ b/EPPlus/ExcelPackage.cs
@@ -42,32 +42,10 @@ namespace OfficeOpenXml; /// <summary> -/// Maps to DotNetZips CompressionLevel enum -/// </summary> -public enum CompressionLevel { - Level0 = 0, - None = 0, - Level1 = 1, - BestSpeed = 1, - Level2 = 2, - Level3 = 3, - Level4 = 4, - Level5 = 5, - Level6 = 6, - Default = 6, - Level7 = 7, - Level8 = 8, - BestCompression = 9, - Level9 = 9, -} - -/// <summary> /// Represents an Excel XLSX file package. -/// This is the top-level object to access all parts of the document.\ +/// This is the top-level object to access all parts of the document. /// </summary> public sealed class ExcelPackage { - internal const bool _preserveWhitespace = false; - /// <summary> /// Extention Schema types /// </summary> @@ -86,8 +64,6 @@ internal const string _schemaRelationships = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; - internal const string _schemaDrawings = "http://schemas.openxmlformats.org/drawingml/2006/main"; - internal const string _schemaMicrosoftVml = "urn:schemas-microsoft-com:vml"; internal const string _schemaMicrosoftOffice = "urn:schemas-microsoft-com:office:office"; internal const string _schemaMicrosoftExcel = "urn:schemas-microsoft-com:office:excel"; @@ -112,18 +88,6 @@ internal const string _schemaVt = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"; - //Pivottables - internal const string _schemaPivotTable = - "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"; - internal const string _schemaPivotCacheDefinition = - "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"; - internal const string _schemaPivotCacheRecords = - "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"; - - internal const string _contentTypeWorkbookDefault = - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; - internal const string _contentTypeWorkbookMacroEnabled = - "application/vnd.ms-excel.sheet.macroEnabled.main+xml"; internal const string _contentTypeSharedString = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; @@ -142,13 +106,7 @@ /// </summary> public ExcelPackage() { Package = new(); - Workbook = CreateWorkbook(); - _ = Workbook.WorkbookXml; - // create the relationship to the main part - Package.CreateRelationship( - UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), ExcelWorkbook.WorkbookUri), - TargetMode.Internal, - _schemaRelationships + "/officeDocument"); + Workbook = new(this, CreateDefaultNsm()); } /// <summary> @@ -157,7 +115,7 @@ public ExcelPackage(FileInfo newFile) { using var inputStream = newFile.OpenRead(); Package = new(inputStream); - Workbook = CreateWorkbook(); + Workbook = new(this, CreateDefaultNsm()); } /// <summary> @@ -165,14 +123,7 @@ /// </summary> public ExcelPackage(Stream newStream) { Package = new(newStream); - Workbook = CreateWorkbook(); - } - - private ExcelWorkbook CreateWorkbook() { - ExcelWorkbook workbook = new(this, CreateDefaultNsm()); - workbook.GetExternalReferences(); - workbook.GetDefinedNames(); - return workbook; + Workbook = new(this, CreateDefaultNsm()); } internal ZipPackage Package { get; } @@ -207,52 +158,54 @@ return ns; } - /// <summary> - /// Saves the XmlDocument into the package at the specified Uri. - /// </summary> - /// <param name="uri">The Uri of the component</param> - /// <param name="xmlDoc">The XmlDocument to save</param> - internal void SavePart(Uri uri, XmlDocument xmlDoc) { - ZipPackagePart part = Package.GetPart(uri); - xmlDoc.Save(part.GetStream(FileMode.Create, FileAccess.Write)); + internal XmlDocument GetXmlDocument(Uri uri) { + var xmlDocument = new XmlDocument(); + var part = Package.GetPart(uri); + XmlHelper.LoadXmlSafe(xmlDocument, part.GetStream()); + part.SaveHandler = writer => xmlDocument.Save(writer); + return xmlDocument; } - /// <summary> - /// Compression option for the package - /// </summary> - public CompressionLevel Compression { - get => Package.Compression; - set => Package.Compression = value; + internal ZipPackageRelationship CreateXmlDocument( + Uri uri, + string contentType, + string relationship, + XmlDocument newDoc) { + Package.CreatePart(uri, contentType, newDoc.Save); + return Package.CreateRelationship( + UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), uri), + TargetMode.Internal, + relationship); } - /// <summary> - /// Get the XmlDocument from an URI - /// </summary> - /// <param name="uri">The Uri to the part</param> - /// <returns>The XmlDocument</returns> - internal XmlDocument GetXmlFromUri(Uri uri) { - XmlDocument xml = new XmlDocument(); - ZipPackagePart part = Package.GetPart(uri); - XmlHelper.LoadXmlSafe(xml, part.GetStream()); - return (xml); + internal XmlDocument GetOrCreateXmlDocument( + Uri uri, + string contentType, + string relationship, + Func<XmlDocument> createDocument) { + if (Package.PartExists(uri)) { + return GetXmlDocument(uri); + } + var newDoc = createDocument(); + CreateXmlDocument(uri, contentType, relationship, newDoc); + return newDoc; } - /// <summary> - /// Saves and returns the Excel files as a bytearray. - /// Note that the package is closed upon save - /// </summary> - /// <example> - /// Example how to return a document from a Webserver... - /// <code> - /// ExcelPackage package=new ExcelPackage(); - /// /**** ... Create the document ****/ - /// Byte[] bin = package.GetAsByteArray(); - /// Response.ContentType = "Application/vnd.ms-Excel"; - /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); - /// Response.BinaryWrite(bin); - /// </code> - /// </example> - /// <returns></returns> + internal XmlDocument GetOrCreateXmlDocument( + Uri uri, + string contentType, + string relationship, + string initialDocumentXml) => + GetOrCreateXmlDocument( + uri, + contentType, + relationship, + () => { + var emptyDocument = new XmlDocument(); + emptyDocument.LoadXml(initialDocumentXml); + return emptyDocument; + }); + public byte[] GetAsByteArray() { var result = new MemoryStream(); Workbook.Save();
diff --git a/EPPlus/ExcelProtectedRange.cs b/EPPlus/ExcelProtectedRange.cs index ef0da39..f89c544 100644 --- a/EPPlus/ExcelProtectedRange.cs +++ b/EPPlus/ExcelProtectedRange.cs
@@ -1,35 +1,14 @@ using System.Xml; -using OfficeOpenXml.Utils; namespace OfficeOpenXml; public class ExcelProtectedRange : XmlHelper { - public string Name { - get => GetXmlNodeString("@name"); - set => SetXmlNodeString("@name", value); - } + public string Name => GetXmlNodeString("@name"); private ExcelAddress _address; - public ExcelAddress Address { - get { - if (_address == null) { - _address = new(GetXmlNodeString("@sqref")); - } - return _address; - } - set { - SetXmlNodeString("@sqref", SqRefUtility.ToSqRefAddress(value.Address)); - _address = value; - } - } - internal ExcelProtectedRange( - string name, - ExcelAddress address, - XmlNamespaceManager ns, - XmlNode topNode) - : base(ns, topNode) { - Name = name; - Address = address; - } + public ExcelAddress Address => _address ??= new(GetXmlNodeString("@sqref")); + + internal ExcelProtectedRange(XmlNamespaceManager ns, XmlNode topNode) + : base(ns, topNode) {} }
diff --git a/EPPlus/ExcelProtectedRangeCollection.cs b/EPPlus/ExcelProtectedRangeCollection.cs index fdc2053..0b95ccf 100644 --- a/EPPlus/ExcelProtectedRangeCollection.cs +++ b/EPPlus/ExcelProtectedRangeCollection.cs
@@ -1,15 +1,11 @@ using System.Collections; using System.Collections.Generic; using System.Xml; -using OfficeOpenXml.Utils; namespace OfficeOpenXml; public class ExcelProtectedRangeCollection : XmlHelper, IEnumerable<ExcelProtectedRange> { - internal ExcelProtectedRangeCollection( - XmlNamespaceManager nsm, - XmlNode topNode, - ExcelWorksheet ws) + internal ExcelProtectedRangeCollection(XmlNamespaceManager nsm, XmlNode topNode) : base(nsm, topNode) { foreach (XmlNode protectedRangeNode in topNode.SelectNodes( "d:protectedRanges/d:protectedRange", @@ -17,64 +13,12 @@ if (!(protectedRangeNode is XmlElement)) { continue; } - _baseList.Add( - new( - protectedRangeNode.Attributes["name"].Value, - new(SqRefUtility.FromSqRefAddress(protectedRangeNode.Attributes["sqref"].Value)), - nsm, - topNode)); + _baseList.Add(new(nsm, topNode)); } } private readonly List<ExcelProtectedRange> _baseList = new(); - public ExcelProtectedRange Add(string name, ExcelAddress address) { - if (!ExistNode("d:protectedRanges")) { - CreateNode("d:protectedRanges"); - } - - var newNode = CreateNode("d:protectedRanges/d:protectedRange"); - var item = new ExcelProtectedRange(name, address, NameSpaceManager, newNode); - _baseList.Add(item); - return item; - } - - public void Clear() { - DeleteNode("d:protectedRanges"); - _baseList.Clear(); - } - - public bool Contains(ExcelProtectedRange item) { - return _baseList.Contains(item); - } - - public void CopyTo(ExcelProtectedRange[] array, int arrayIndex) { - _baseList.CopyTo(array, arrayIndex); - } - - public int Count => _baseList.Count; - - public bool Remove(ExcelProtectedRange item) { - DeleteAllNode( - "d:protectedRanges/d:protectedRange[@name='" - + item.Name - + "' and @sqref='" - + item.Address.Address - + "']"); - if (_baseList.Count == 0) { - DeleteNode("d:protectedRanges"); - } - return _baseList.Remove(item); - } - - public int IndexOf(ExcelProtectedRange item) { - return _baseList.IndexOf(item); - } - - public void RemoveAt(int index) { - _baseList.RemoveAt(index); - } - public ExcelProtectedRange this[int index] => _baseList[index]; IEnumerator<ExcelProtectedRange> IEnumerable<ExcelProtectedRange>.GetEnumerator() {
diff --git a/EPPlus/ExcelProtection.cs b/EPPlus/ExcelProtection.cs index bf10612..5d0c943 100644 --- a/EPPlus/ExcelProtection.cs +++ b/EPPlus/ExcelProtection.cs
@@ -30,6 +30,7 @@ * Jan Källman License changed GPL-->LGPL 2011-12-27 *******************************************************************************/ +using System.Collections.Immutable; using System.Xml; namespace OfficeOpenXml; @@ -40,10 +41,11 @@ ///<seealso cref="ExcelSheetProtection"/> /// </summary> public class ExcelProtection : XmlHelper { - internal ExcelProtection(XmlNamespaceManager ns, XmlNode topNode, ExcelWorkbook wb) - : base(ns, topNode) { - SchemaNodeOrder = wb.SchemaNodeOrder; - } + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorkbook.WorkbookSchemaNodeOrder; + + internal ExcelProtection(XmlNamespaceManager ns, XmlNode topNode) + : base(ns, topNode) {} private const string _lockStructurePath = "d:workbookProtection/@lockStructure";
diff --git a/EPPlus/ExcelRangeBase.cs b/EPPlus/ExcelRangeBase.cs index 686eb78..9c82531 100644 --- a/EPPlus/ExcelRangeBase.cs +++ b/EPPlus/ExcelRangeBase.cs
@@ -34,23 +34,15 @@ using System; using System.Collections; using System.Collections.Generic; -using System.ComponentModel; -using System.Data; using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; using System.Security; using System.Text; -using System.Text.RegularExpressions; -using System.Threading; using System.Xml; using OfficeOpenXml.ConditionalFormatting; using OfficeOpenXml.DataValidation; using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using OfficeOpenXml.Style; using OfficeOpenXml.Style.XmlAccess; -using OfficeOpenXml.Table; namespace OfficeOpenXml; @@ -1310,460 +1302,6 @@ public IRangeDataValidation DataValidation => new RangeDataValidation(_worksheet, Address); /// <summary> - /// Load the data from the datareader starting from the top left cell of the range - /// </summary> - /// <param name="reader">The datareader to loadfrom</param> - /// <param name="printHeaders">Print the column caption property (if set) or the columnname property if not, on first row</param> - /// <param name="tableName">The name of the table</param> - /// <param name="tableStyle">The table style to apply to the data</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromDataReader( - IDataReader reader, - bool printHeaders, - string tableName, - TableStyles tableStyle = TableStyles.None) { - var r = LoadFromDataReader(reader, printHeaders); - - int rows = r.Rows - 1; - if (rows >= 0 && r.Columns > 0) { - var tbl = _worksheet.Tables.Add( - new(_fromRow, _fromCol, _fromRow + (rows <= 0 ? 1 : rows), _fromCol + r.Columns - 1), - tableName); - tbl.ShowHeader = printHeaders; - tbl.TableStyle = tableStyle; - } - return r; - } - - /// <summary> - /// Load the data from the datareader starting from the top left cell of the range - /// </summary> - /// <param name="reader">The datareader to load from</param> - /// <param name="printHeaders">Print the caption property (if set) or the columnname property if not, on first row</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromDataReader(IDataReader reader, bool printHeaders) { - if (reader == null) { - throw (new ArgumentNullException("reader", "Reader can't be null")); - } - int fieldCount = reader.FieldCount; - - int col = _fromCol, - row = _fromRow; - if (printHeaders) { - for (int i = 0; i < fieldCount; i++) { - // If no caption is set, the ColumnName property is called implicitly. - _worksheet._values.SetValue(row, col++, reader.GetName(i)); - } - row++; - col = _fromCol; - } - while (reader.Read()) { - for (int i = 0; i < fieldCount; i++) { - _worksheet._values.SetValue(row, col++, reader.GetValue(i)); - } - row++; - col = _fromCol; - } - return _worksheet.Cells[_fromRow, _fromCol, row - 1, _fromCol + fieldCount - 1]; - } - - /// <summary> - /// Load the data from the datatable starting from the top left cell of the range - /// </summary> - /// <param name="table">The datatable to load</param> - /// <param name="printHeaders">Print the column caption property (if set) or the columnname property if not, on first row</param> - /// <param name="tableStyle">The table style to apply to the data</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromDataTable( - DataTable table, - bool printHeaders, - TableStyles tableStyle) { - var r = LoadFromDataTable(table, printHeaders); - - int rows = (table.Rows.Count == 0 ? 1 : table.Rows.Count) + (printHeaders ? 1 : 0); - if (rows >= 0 && table.Columns.Count > 0) { - var tbl = _worksheet.Tables.Add( - new(_fromRow, _fromCol, _fromRow + rows - 1, _fromCol + table.Columns.Count - 1), - table.TableName); - tbl.ShowHeader = printHeaders; - tbl.TableStyle = tableStyle; - } - return r; - } - - /// <summary> - /// Load the data from the datatable starting from the top left cell of the range - /// </summary> - /// <param name="table">The datatable to load</param> - /// <param name="printHeaders">Print the caption property (if set) or the columnname property if not, on first row</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromDataTable(DataTable table, bool printHeaders) { - if (table == null) { - throw (new ArgumentNullException("Table can't be null")); - } - - int col = _fromCol, - row = _fromRow; - if (printHeaders) { - foreach (DataColumn dc in table.Columns) { - // If no caption is set, the ColumnName property is called implicitly. - _worksheet._values.SetValue(row, col++, dc.Caption); - } - row++; - col = _fromCol; - } else if (table.Rows.Count == 0) { - return null; - } - foreach (DataRow dr in table.Rows) { - foreach (object value in dr.ItemArray) { - if (value != null && value != DBNull.Value && !string.IsNullOrEmpty(value.ToString())) { - _worksheet._values.SetValue(row, col++, value); - } else { - col++; - } - } - row++; - col = _fromCol; - } - return _worksheet.Cells[_fromRow, - _fromCol, - (row == _fromRow ? _fromRow : row - 1), - _fromCol + table.Columns.Count - 1]; - } - - /// <summary> - /// Loads data from the collection of arrays of objects into the range, starting from - /// the top-left cell. - /// </summary> - /// <param name="data">The data.</param> - public ExcelRangeBase LoadFromArrays(IEnumerable<object[]> data) { - //thanx to Abdullin for the code contribution - if (data == null) { - throw new ArgumentNullException("data"); - } - - int column = _fromCol, - row = _fromRow; - - foreach (var rowData in data) { - column = _fromCol; - foreach (var cellData in rowData) { - _worksheet._values.SetValue(row, column, cellData); - column += 1; - } - row += 1; - } - return _worksheet.Cells[_fromRow, _fromCol, row - 1, column - 1]; - } - - /// <summary> - /// Load a collection into a the worksheet starting from the top left row of the range. - /// </summary> - /// <typeparam name="T">The datatype in the collection</typeparam> - /// <param name="collection">The collection to load</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromCollection<T>(IEnumerable<T> collection) { - return LoadFromCollection( - collection, - false, - TableStyles.None, - BindingFlags.Public | BindingFlags.Instance, - null); - } - - /// <summary> - /// Load a collection of T into the worksheet starting from the top left row of the range. - /// Default option will load all public instance properties of T - /// </summary> - /// <typeparam name="T">The datatype in the collection</typeparam> - /// <param name="collection">The collection to load</param> - /// <param name="printHeaders">Print the property names on the first row. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromCollection<T>(IEnumerable<T> collection, bool printHeaders) { - return LoadFromCollection( - collection, - printHeaders, - TableStyles.None, - BindingFlags.Public | BindingFlags.Instance, - null); - } - - /// <summary> - /// Load a collection of T into the worksheet starting from the top left row of the range. - /// Default option will load all public instance properties of T - /// </summary> - /// <typeparam name="T">The datatype in the collection</typeparam> - /// <param name="collection">The collection to load</param> - /// <param name="printHeaders">Print the property names on the first row. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param> - /// <param name="tableStyle">Will create a table with this style. If set to TableStyles.None no table will be created</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromCollection<T>( - IEnumerable<T> collection, - bool printHeaders, - TableStyles tableStyle) { - return LoadFromCollection( - collection, - printHeaders, - tableStyle, - BindingFlags.Public | BindingFlags.Instance, - null); - } - - /// <summary> - /// Load a collection into the worksheet starting from the top left row of the range. - /// </summary> - /// <typeparam name="T">The datatype in the collection</typeparam> - /// <param name="collection">The collection to load</param> - /// <param name="printHeaders">Print the property names on the first row. Any underscore in the property name will be converted to a space. If the property is decorated with a <see cref="DisplayNameAttribute"/> or a <see cref="DescriptionAttribute"/> that attribute will be used instead of the reflected member name.</param> - /// <param name="tableStyle">Will create a table with this style. If set to TableStyles.None no table will be created</param> - /// <param name="memberFlags">Property flags to use</param> - /// <param name="members">The properties to output. Must be of type T</param> - /// <returns>The filled range</returns> - public ExcelRangeBase LoadFromCollection<T>( - IEnumerable<T> collection, - bool printHeaders, - TableStyles tableStyle, - BindingFlags memberFlags, - MemberInfo[] members) { - var type = typeof(T); - if (members == null) { - members = type.GetProperties(memberFlags); - } else { - foreach (var t in members) { - if (t.DeclaringType != null - && t.DeclaringType != type - && !t.DeclaringType.IsSubclassOf(type)) { - throw new InvalidCastException( - "Supplied properties in parameter Properties must be of the same type as T (or an assignable type from T"); - } - } - } - - int col = _fromCol, - row = _fromRow; - if (members.Length > 0 && printHeaders) { - foreach (var t in members) { - var descriptionAttribute = - t.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() - as DescriptionAttribute; - var header = string.Empty; - if (descriptionAttribute != null) { - header = descriptionAttribute.Description; - } else { - var displayNameAttribute = - t.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault() - as DisplayNameAttribute; - if (displayNameAttribute != null) { - header = displayNameAttribute.DisplayName; - } else { - header = t.Name.Replace('_', ' '); - } - } - _worksheet._values.SetValue(row, col++, header); - } - row++; - } - - if (!collection.Any() && (members.Length == 0 || printHeaders == false)) { - return null; - } - - if (members.Length == 0) { - foreach (var item in collection) { - _worksheet.Cells[row++, col].Value = item; - } - } else { - foreach (var item in collection) { - col = _fromCol; - if (item is string || item is decimal || item is DateTime || item.GetType().IsPrimitive) { - _worksheet.Cells[row, col++].Value = item; - } else { - foreach (var t in members) { - if (t is PropertyInfo info) { - _worksheet.Cells[row, col++].Value = info.GetValue(item, null); - } else if (t is FieldInfo fieldInfo) { - _worksheet.Cells[row, col++].Value = fieldInfo.GetValue(item); - } else if (t is MethodInfo methodInfo) { - _worksheet.Cells[row, col++].Value = methodInfo.Invoke(item, null); - } - } - } - row++; - } - } - - if (_fromRow == row - 1 && printHeaders) { - row++; - } - - var r = _worksheet.Cells[_fromRow, _fromCol, row - 1, members.Length == 0 ? col : col - 1]; - - if (tableStyle != TableStyles.None) { - var tbl = _worksheet.Tables.Add(r, ""); - tbl.ShowHeader = printHeaders; - tbl.TableStyle = tableStyle; - } - return r; - } - - /// <summary> - /// Loads a CSV text into a range starting from the top left cell. - /// Default settings is Comma separation - /// </summary> - /// <param name="text">The Text</param> - /// <returns>The range containing the data</returns> - public ExcelRangeBase LoadFromText(string text) { - return LoadFromText(text, new()); - } - - /// <summary> - /// Loads a CSV text into a range starting from the top left cell. - /// </summary> - /// <param name="text">The Text</param> - /// <param name="format">Information how to load the text</param> - /// <returns>The range containing the data</returns> - public ExcelRangeBase LoadFromText(string text, ExcelTextFormat format) { - if (string.IsNullOrEmpty(text)) { - var r = _worksheet.Cells[_fromRow, _fromCol]; - r.Value = ""; - return r; - } - - if (format == null) { - format = new(); - } - - string splitRegex = String.Format( - "{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*[^{1}]*$)", - format.EOL, - format.TextQualifier); - string[] lines = Regex.Split(text, splitRegex); - int row = _fromRow; - int col = _fromCol; - int maxCol = col; - int lineNo = 1; - foreach (string line in lines) { - if (lineNo > format.SkipLinesBeginning && lineNo <= lines.Length - format.SkipLinesEnd) { - col = _fromCol; - string v = ""; - bool isText = false, - isQualifier = false; - int qCount = 0; - int lineQCount = 0; - foreach (char c in line) { - if (format.TextQualifier != 0 && c == format.TextQualifier) { - if (!isText && v != "") { - throw (new(string.Format("Invalid Text Qualifier in line : {0}", line))); - } - isQualifier = !isQualifier; - qCount += 1; - lineQCount++; - isText = true; - } else { - if (qCount > 1 && !string.IsNullOrEmpty(v)) { - v += new string(format.TextQualifier, qCount / 2); - } else if (qCount > 2 && string.IsNullOrEmpty(v)) { - v += new string(format.TextQualifier, (qCount - 1) / 2); - } - - if (isQualifier) { - v += c; - } else { - if (c == format.Delimiter) { - _worksheet.SetValue(row, col, ConvertData(format, v, col - _fromCol, isText)); - v = ""; - isText = false; - col++; - } else { - if (qCount % 2 == 1) { - throw (new(string.Format("Text delimiter is not closed in line : {0}", line))); - } - v += c; - } - } - qCount = 0; - } - } - if (qCount > 1) { - v += new string(format.TextQualifier, qCount / 2); - } - if (lineQCount % 2 == 1) { - throw (new(string.Format("Text delimiter is not closed in line : {0}", line))); - } - - _worksheet._values.SetValue(row, col, ConvertData(format, v, col - _fromCol, isText)); - if (col > maxCol) { - maxCol = col; - } - row++; - } - lineNo++; - } - return _worksheet.Cells[_fromRow, _fromCol, row - 1, maxCol]; - } - - /// <summary> - /// Loads a CSV text into a range starting from the top left cell. - /// </summary> - /// <param name="text">The Text</param> - /// <param name="format">Information how to load the text</param> - /// <param name="tableStyle">Create a table with this style</param> - /// <param name="firstRowIsHeader">Use the first row as header</param> - /// <returns></returns> - public ExcelRangeBase LoadFromText( - string text, - ExcelTextFormat format, - TableStyles tableStyle, - bool firstRowIsHeader) { - var r = LoadFromText(text, format); - - var tbl = _worksheet.Tables.Add(r, ""); - tbl.ShowHeader = firstRowIsHeader; - tbl.TableStyle = tableStyle; - - return r; - } - - /// <summary> - /// Loads a CSV file into a range starting from the top left cell. - /// </summary> - /// <param name="textFile">The Textfile</param> - /// <returns></returns> - public ExcelRangeBase LoadFromText(FileInfo textFile) { - return LoadFromText(File.ReadAllText(textFile.FullName, Encoding.ASCII)); - } - - /// <summary> - /// Loads a CSV file into a range starting from the top left cell. - /// </summary> - /// <param name="textFile">The Textfile</param> - /// <param name="format">Information how to load the text</param> - /// <returns></returns> - public ExcelRangeBase LoadFromText(FileInfo textFile, ExcelTextFormat format) { - return LoadFromText(File.ReadAllText(textFile.FullName, format.Encoding), format); - } - - /// <summary> - /// Loads a CSV file into a range starting from the top left cell. - /// </summary> - /// <param name="textFile">The Textfile</param> - /// <param name="format">Information how to load the text</param> - /// <param name="tableStyle">Create a table with this style</param> - /// <param name="firstRowIsHeader">Use the first row as header</param> - /// <returns></returns> - public ExcelRangeBase LoadFromText( - FileInfo textFile, - ExcelTextFormat format, - TableStyles tableStyle, - bool firstRowIsHeader) { - return LoadFromText( - File.ReadAllText(textFile.FullName, format.Encoding), - format, - tableStyle, - firstRowIsHeader); - } - - /// <summary> /// Get the strongly typed value of the cell. /// </summary> /// <typeparam name="T">The type</typeparam>
diff --git a/EPPlus/ExcelRow.cs b/EPPlus/ExcelRow.cs index bb94e2b..f4336c7 100644 --- a/EPPlus/ExcelRow.cs +++ b/EPPlus/ExcelRow.cs
@@ -44,20 +44,6 @@ internal bool PageBreak; internal bool Phonetic; internal bool CustomHeight; - internal int MergeID; - - internal RowInternal Clone() { - return new() { - Height = Height, - Hidden = Hidden, - Collapsed = Collapsed, - OutlineLevel = OutlineLevel, - PageBreak = PageBreak, - Phonetic = Phonetic, - CustomHeight = CustomHeight, - MergeID = MergeID, - }; - } } /// <summary> @@ -269,20 +255,4 @@ get => RowID; set => Row = ((int)(value >> 29)); } - - /// <summary> - /// Copies the current row to a new worksheet - /// </summary> - /// <param name="added">The worksheet where the copy will be created</param> - internal void Clone(ExcelWorksheet added) { - ExcelRow newRow = added.Row(Row); - newRow.Collapsed = Collapsed; - newRow.Height = Height; - newRow.Hidden = Hidden; - newRow.OutlineLevel = OutlineLevel; - newRow.PageBreak = PageBreak; - newRow.Phonetic = Phonetic; - newRow._styleName = _styleName; - newRow.StyleID = StyleID; - } }
diff --git a/EPPlus/ExcelSheetProtection.cs b/EPPlus/ExcelSheetProtection.cs index bc7996d..0b2cb09 100644 --- a/EPPlus/ExcelSheetProtection.cs +++ b/EPPlus/ExcelSheetProtection.cs
@@ -30,6 +30,7 @@ * Jan Källman License changed GPL-->LGPL 2011-12-27 *******************************************************************************/ +using System.Collections.Immutable; using System.Xml; namespace OfficeOpenXml; @@ -40,10 +41,11 @@ ///<seealso cref="ExcelProtection"/> /// </summary> public sealed class ExcelSheetProtection : XmlHelper { - internal ExcelSheetProtection(XmlNamespaceManager nsm, XmlNode topNode, ExcelWorksheet ws) - : base(nsm, topNode) { - SchemaNodeOrder = ws.SchemaNodeOrder; - } + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorksheet.WorksheetSchemaNodeOrder; + + internal ExcelSheetProtection(XmlNamespaceManager nsm, XmlNode topNode) + : base(nsm, topNode) {} private const string _isProtectedPath = "d:sheetProtection/@sheet";
diff --git a/EPPlus/ExcelStyles.cs b/EPPlus/ExcelStyles.cs index 9f1b283..6bb75ff 100644 --- a/EPPlus/ExcelStyles.cs +++ b/EPPlus/ExcelStyles.cs
@@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Xml; using OfficeOpenXml.ConditionalFormatting; @@ -60,21 +61,22 @@ private readonly XmlNamespaceManager _nameSpaceManager; internal int _nextDfxNumFmtID = 164; + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "numFmts", + "fonts", + "fills", + "borders", + "cellStyleXfs", + "cellXfs", + "cellStyles", + "dxfs", + ]; + internal ExcelStyles(XmlNamespaceManager nameSpaceManager, XmlDocument xml, ExcelWorkbook wb) : base(nameSpaceManager, xml) { _styleXml = xml; _wb = wb; _nameSpaceManager = nameSpaceManager; - SchemaNodeOrder = new[] { - "numFmts", - "fonts", - "fills", - "borders", - "cellStyleXfs", - "cellXfs", - "cellStyles", - "dxfs", - }; LoadFromDocument(); } @@ -177,13 +179,11 @@ var ws = _wb.Worksheets[e.PositionID]; Dictionary<int, int> styleCashe = new Dictionary<int, int>(); //Set single address - lock (ws._styles) { - SetStyleAddress(sender, e, address, ws, ref styleCashe); - if (address.Addresses != null) { - //Handle multiaddresses - foreach (var innerAddress in address.Addresses) { - SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe); - } + SetStyleAddress(sender, e, address, ws, ref styleCashe); + if (address.Addresses != null) { + //Handle multiaddresses + foreach (var innerAddress in address.Addresses) { + SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe); } } return 0; @@ -769,113 +769,106 @@ } internal int CloneStyle(ExcelStyles style, int styleId, bool isNamedStyle, bool allwaysAdd) { - ExcelXfs xfs; - lock (style) { - if (isNamedStyle) { - xfs = style.CellStyleXfs[styleId]; - } else { - xfs = style.CellXfs[styleId]; - } - ExcelXfs newXfs = xfs.Copy(this); - //Numberformat - if (xfs.NumberFormatId > 0) { - //rake36: Two problems here... - //rake36: 1. the first time through when format stays equal to String.Empty, it adds a string.empty to the list of Number Formats - //rake36: 2. when adding a second sheet, if the numberformatid == 164, it finds the 164 added by previous sheets but was using the array index - //rake36: for the numberformatid + var xfs = isNamedStyle ? style.CellStyleXfs[styleId] : style.CellXfs[styleId]; + ExcelXfs newXfs = xfs.Copy(this); + //Numberformat + if (xfs.NumberFormatId > 0) { + //rake36: Two problems here... + //rake36: 1. the first time through when format stays equal to String.Empty, it adds a string.empty to the list of Number Formats + //rake36: 2. when adding a second sheet, if the numberformatid == 164, it finds the 164 added by previous sheets but was using the array index + //rake36: for the numberformatid - string format = string.Empty; - foreach (var fmt in style.NumberFormats) { - if (fmt.NumFmtId == xfs.NumberFormatId) { - format = fmt.Format; - break; - } - } - //rake36: Don't add another format if it's blank - if (!String.IsNullOrEmpty(format)) { - int ix = NumberFormats.FindIndexById(format); - if (ix < 0) { - var item = new ExcelNumberFormatXml(NameSpaceManager) { - Format = format, - NumFmtId = NumberFormats.NextId++, - }; - NumberFormats.Add(format, item); - //rake36: Use the just added format id - newXfs.NumberFormatId = item.NumFmtId; - } else { - //rake36: Use the format id defined by the index... not the index itself - newXfs.NumberFormatId = NumberFormats[ix].NumFmtId; - } + string format = string.Empty; + foreach (var fmt in style.NumberFormats) { + if (fmt.NumFmtId == xfs.NumberFormatId) { + format = fmt.Format; + break; } } - - //Font - if (xfs.FontId > -1) { - int ix = Fonts.FindIndexById(xfs.Font.Id); + //rake36: Don't add another format if it's blank + if (!String.IsNullOrEmpty(format)) { + int ix = NumberFormats.FindIndexById(format); if (ix < 0) { - ExcelFontXml item = style.Fonts[xfs.FontId].Copy(); - ix = Fonts.Add(xfs.Font.Id, item); - } - newXfs.FontId = ix; - } - - //Border - if (xfs.BorderId > -1) { - int ix = Borders.FindIndexById(xfs.Border.Id); - if (ix < 0) { - ExcelBorderXml item = style.Borders[xfs.BorderId].Copy(); - ix = Borders.Add(xfs.Border.Id, item); - } - newXfs.BorderId = ix; - } - - //Fill - if (xfs.FillId > -1) { - int ix = Fills.FindIndexById(xfs.Fill.Id); - if (ix < 0) { - var item = style.Fills[xfs.FillId].Copy(); - ix = Fills.Add(xfs.Fill.Id, item); - } - newXfs.FillId = ix; - } - - //Named style reference - if (xfs.XfId > 0) { - var id = style.CellStyleXfs[xfs.XfId].Id; - var newId = CellStyleXfs.FindIndexById(id); - if (newId >= 0) { - newXfs.XfId = newId; - } else if (style._wb != _wb - && allwaysAdd - == false) //Not the same workbook, copy the namedstyle to the workbook or match the id - { - var nsFind = style.NamedStyles.ToDictionary(d => (d.StyleXfId)); - if (nsFind.ContainsKey(xfs.XfId)) { - var st = nsFind[xfs.XfId]; - if (NamedStyles.ExistsKey(st.Name)) { - newXfs.XfId = NamedStyles.FindIndexById(st.Name); - } else { - var ns = CreateNamedStyle(st.Name, st.Style); - newXfs.XfId = NamedStyles.Count - 1; - } - } - } - } - - int index; - if (isNamedStyle) { - index = CellStyleXfs.Add(newXfs.Id, newXfs); - } else { - if (allwaysAdd) { - index = CellXfs.Add(newXfs.Id, newXfs); + var item = new ExcelNumberFormatXml(NameSpaceManager) { + Format = format, + NumFmtId = NumberFormats.NextId++, + }; + NumberFormats.Add(format, item); + //rake36: Use the just added format id + newXfs.NumberFormatId = item.NumFmtId; } else { - index = CellXfs.FindIndexById(newXfs.Id); - if (index < 0) { - index = CellXfs.Add(newXfs.Id, newXfs); + //rake36: Use the format id defined by the index... not the index itself + newXfs.NumberFormatId = NumberFormats[ix].NumFmtId; + } + } + } + + //Font + if (xfs.FontId > -1) { + int ix = Fonts.FindIndexById(xfs.Font.Id); + if (ix < 0) { + ExcelFontXml item = style.Fonts[xfs.FontId].Copy(); + ix = Fonts.Add(xfs.Font.Id, item); + } + newXfs.FontId = ix; + } + + //Border + if (xfs.BorderId > -1) { + int ix = Borders.FindIndexById(xfs.Border.Id); + if (ix < 0) { + ExcelBorderXml item = style.Borders[xfs.BorderId].Copy(); + ix = Borders.Add(xfs.Border.Id, item); + } + newXfs.BorderId = ix; + } + + //Fill + if (xfs.FillId > -1) { + int ix = Fills.FindIndexById(xfs.Fill.Id); + if (ix < 0) { + var item = style.Fills[xfs.FillId].Copy(); + ix = Fills.Add(xfs.Fill.Id, item); + } + newXfs.FillId = ix; + } + + //Named style reference + if (xfs.XfId > 0) { + var id = style.CellStyleXfs[xfs.XfId].Id; + var newId = CellStyleXfs.FindIndexById(id); + if (newId >= 0) { + newXfs.XfId = newId; + } else if (style._wb != _wb + && allwaysAdd + == false) //Not the same workbook, copy the namedstyle to the workbook or match the id + { + var nsFind = style.NamedStyles.ToDictionary(d => (d.StyleXfId)); + if (nsFind.ContainsKey(xfs.XfId)) { + var st = nsFind[xfs.XfId]; + if (NamedStyles.ExistsKey(st.Name)) { + newXfs.XfId = NamedStyles.FindIndexById(st.Name); + } else { + var ns = CreateNamedStyle(st.Name, st.Style); + newXfs.XfId = NamedStyles.Count - 1; } } } - return index; } + + int index; + if (isNamedStyle) { + index = CellStyleXfs.Add(newXfs.Id, newXfs); + } else { + if (allwaysAdd) { + index = CellXfs.Add(newXfs.Id, newXfs); + } else { + index = CellXfs.FindIndexById(newXfs.Id); + if (index < 0) { + index = CellXfs.Add(newXfs.Id, newXfs); + } + } + } + return index; } }
diff --git a/EPPlus/ExcelTextFormat.cs b/EPPlus/ExcelTextFormat.cs index 83b0c59..8029447 100644 --- a/EPPlus/ExcelTextFormat.cs +++ b/EPPlus/ExcelTextFormat.cs
@@ -86,54 +86,45 @@ /// <item><term>Encoding</term><description>Encoding.ASCII</description></item> /// </list> /// </summary> - public ExcelTextFormat() { - Delimiter = ','; - TextQualifier = '\0'; - EOL = "\r\n"; - Culture = CultureInfo.InvariantCulture; - DataTypes = null; - SkipLinesBeginning = 0; - SkipLinesEnd = 0; - Encoding = Encoding.ASCII; - } + public ExcelTextFormat() {} /// <summary> /// Delimiter character /// </summary> - public char Delimiter { get; set; } + public char Delimiter { get; set; } = ','; /// <summary> /// Text qualifier character /// </summary> - public char TextQualifier { get; set; } + public char TextQualifier { get; set; } = '\0'; /// <summary> /// End of line characters. Default CRLF /// </summary> - public string EOL { get; set; } + public string EOL { get; set; } = "\r\n"; /// <summary> /// Datatypes list for each column (if column is not present Unknown is assumed) /// </summary> - public eDataTypes[] DataTypes { get; set; } + public eDataTypes[] DataTypes { get; set; } = null; /// <summary> /// Culture used when parsing. Default CultureInfo.InvariantCulture /// </summary> - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// <summary> /// Number of lines skiped in the begining of the file. Default 0. /// </summary> - public int SkipLinesBeginning { get; set; } + public int SkipLinesBeginning { get; set; } = 0; /// <summary> /// Number of lines skiped at the end of the file. Default 0. /// </summary> - public int SkipLinesEnd { get; set; } + public int SkipLinesEnd { get; set; } = 0; /// <summary> /// Only used when reading files from disk using a FileInfo object. Default AscII /// </summary> - public Encoding Encoding { get; set; } + public Encoding Encoding { get; set; } = Encoding.ASCII; }
diff --git a/EPPlus/ExcelWorkbook.cs b/EPPlus/ExcelWorkbook.cs index 3331644..c581319 100644 --- a/EPPlus/ExcelWorkbook.cs +++ b/EPPlus/ExcelWorkbook.cs
@@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Text; @@ -77,57 +78,105 @@ internal bool isRichText; } - internal ExcelPackage _package; + private readonly ExcelPackage _package; private ExcelWorksheets _worksheets; private OfficeProperties _properties; private ExcelStyles _styles; - /// <summary> - /// Creates a new instance of the ExcelWorkbook class. - /// </summary> - /// <param name="package">The parent package</param> - /// <param name="namespaceManager">NamespaceManager</param> + internal static ImmutableArray<string> WorkbookSchemaNodeOrder = [ + "fileVersion", + "fileSharing", + "workbookPr", + "workbookProtection", + "bookViews", + "sheets", + "functionGroups", + "functionPrototypes", + "externalReferences", + "definedNames", + "calcPr", + "oleSize", + "customWorkbookViews", + "pivotCaches", + "smartTagPr", + "smartTagTypes", + "webPublishing", + "fileRecoveryPr", + ]; + + protected override ImmutableArray<string> SchemaNodeOrder => WorkbookSchemaNodeOrder; + internal ExcelWorkbook(ExcelPackage package, XmlNamespaceManager namespaceManager) : base(namespaceManager) { _package = package; - _names = new(this); _namespaceManager = namespaceManager; + + WorkbookXml = package.GetOrCreateXmlDocument( + WorkbookUri, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", + ExcelPackage._schemaRelationships + "/officeDocument", + () => CreateEmptyWorkbookXml(namespaceManager)); + _stylesXml = package.GetOrCreateXmlDocument( + StylesUri, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", + ExcelPackage._schemaRelationships + "/styles", + CreateEmptyStylesXml); + TopNode = WorkbookXml.DocumentElement; - SchemaNodeOrder = new[] { - "fileVersion", - "fileSharing", - "workbookPr", - "workbookProtection", - "bookViews", - "sheets", - "functionGroups", - "functionPrototypes", - "externalReferences", - "definedNames", - "calcPr", - "oleSize", - "customWorkbookViews", - "pivotCaches", - "smartTagPr", - "smartTagTypes", - "webPublishing", - "fileRecoveryPr", - }; FullCalcOnLoad = true; //Full calculation on load by default, for both new workbooks and templates. + GetSharedStrings(); + GetExternalReferences(); + GetDefinedNames(); } - internal Dictionary<string, SharedStringItem> _sharedStrings = new(); //Used when reading cells. + private static XmlDocument CreateEmptyWorkbookXml(XmlNamespaceManager namespaceManager) { + var result = new XmlDocument(namespaceManager.NameTable); + var wbElem = result.CreateElement("workbook", ExcelPackage._schemaMain); + + // Add the relationships namespace + wbElem.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships); + result.AppendChild(wbElem); + + // Create the bookViews and workbooks element + var bookViews = result.CreateElement("bookViews", ExcelPackage._schemaMain); + wbElem.AppendChild(bookViews); + var workbookView = result.CreateElement("workbookView", ExcelPackage._schemaMain); + bookViews.AppendChild(workbookView); + + return result; + } + + private static XmlDocument CreateEmptyStylesXml() { + StringBuilder xml = new StringBuilder( + "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"); + xml.Append("<numFmts />"); + xml.Append("<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>"); + xml.Append( + "<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>"); + xml.Append( + "<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>"); + xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>"); + xml.Append("<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>"); + xml.Append("<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>"); + xml.Append("<dxfs count=\"0\" />"); + xml.Append("</styleSheet>"); + + var result = new XmlDocument(); + result.LoadXml(xml.ToString()); + return result; + } + + internal readonly Dictionary<string, SharedStringItem> _sharedStrings = new(); //Used when reading cells. internal List<SharedStringItem> _sharedStringsList = new(); //Used when reading cells. internal ExcelNamedRangeCollection _names; - internal int _nextDrawingID = 0; internal int _nextTableID = int.MinValue; internal int _nextPivotTableID = int.MinValue; - internal XmlNamespaceManager _namespaceManager; - internal FormulaParser _formulaParser; - internal FormulaParserManager _parserManager; + private readonly XmlNamespaceManager _namespaceManager; + private FormulaParser _formulaParser; + private FormulaParserManager _parserManager; internal CellStore<List<Token>> _formulaTokens; /// <summary> @@ -135,7 +184,7 @@ /// </summary> private void GetSharedStrings() { if (_package.Package.PartExists(SharedStringsUri)) { - var xml = _package.GetXmlFromUri(SharedStringsUri); + var xml = _package.GetXmlDocument(SharedStringsUri); XmlNodeList nl = xml.SelectNodes("//d:sst/d:si", NameSpaceManager); _sharedStringsList = new(); if (nl != null) { @@ -273,7 +322,7 @@ public ExcelWorksheets Worksheets { get { if (_worksheets == null) { - var sheetsNode = _workbookXml.DocumentElement.SelectSingleNode( + var sheetsNode = WorkbookXml.DocumentElement.SelectSingleNode( "d:sheets", _namespaceManager); if (sheetsNode == null) { @@ -314,15 +363,7 @@ /// <summary> /// Access properties to protect or unprotect a workbook /// </summary> - public ExcelProtection Protection { - get { - if (_protection == null) { - _protection = new(NameSpaceManager, TopNode, this); - _protection.SchemaNodeOrder = SchemaNodeOrder; - } - return _protection; - } - } + public ExcelProtection Protection => _protection ??= new(NameSpaceManager, TopNode); private ExcelWorkbookView _view; @@ -346,31 +387,22 @@ /// <summary> /// URI to the styles inside the package /// </summary> - internal static Uri StylesUri { get; } = new("/xl/styles.xml", UriKind.Relative); + private static Uri StylesUri { get; } = new("/xl/styles.xml", UriKind.Relative); /// <summary> /// URI to the shared strings inside the package /// </summary> - internal static Uri SharedStringsUri { get; } = new("/xl/sharedStrings.xml", UriKind.Relative); + private static Uri SharedStringsUri { get; } = new("/xl/sharedStrings.xml", UriKind.Relative); /// <summary> /// Returns a reference to the workbook's part within the package /// </summary> internal ZipPackagePart Part => (_package.Package.GetPart(WorkbookUri)); - private XmlDocument _workbookXml; - /// <summary> /// Provides access to the XML data representing the workbook in the package. /// </summary> - public XmlDocument WorkbookXml { - get { - if (_workbookXml == null) { - CreateWorkbookXml(_namespaceManager); - } - return (_workbookXml); - } - } + internal XmlDocument WorkbookXml { get; } private const string _codeModuleNamePath = "d:workbookPr/@codeName"; @@ -405,101 +437,7 @@ } } - /// <summary> - /// Create or read the XML for the workbook. - /// </summary> - private void CreateWorkbookXml(XmlNamespaceManager namespaceManager) { - if (_package.Package.PartExists(WorkbookUri)) { - _workbookXml = _package.GetXmlFromUri(WorkbookUri); - } else { - // create a new workbook part and add to the package - ZipPackagePart partWorkbook = _package.Package.CreatePart( - WorkbookUri, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", - _package.Compression); - - // create the workbook - _workbookXml = new(namespaceManager.NameTable); - - _workbookXml.PreserveWhitespace = ExcelPackage._preserveWhitespace; - // create the workbook element - XmlElement wbElem = _workbookXml.CreateElement("workbook", ExcelPackage._schemaMain); - - // Add the relationships namespace - wbElem.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships); - - _workbookXml.AppendChild(wbElem); - - // create the bookViews and workbooks element - XmlElement bookViews = _workbookXml.CreateElement("bookViews", ExcelPackage._schemaMain); - wbElem.AppendChild(bookViews); - XmlElement workbookView = _workbookXml.CreateElement( - "workbookView", - ExcelPackage._schemaMain); - bookViews.AppendChild(workbookView); - - // save it to the package - StreamWriter stream = new StreamWriter( - partWorkbook.GetStream(FileMode.Create, FileAccess.Write)); - _workbookXml.Save(stream); - } - } - - private XmlDocument _stylesXml; - - /// <summary> - /// Provides access to the XML data representing the styles in the package. - /// </summary> - public XmlDocument StylesXml { - get { - if (_stylesXml == null) { - if (_package.Package.PartExists(StylesUri)) { - _stylesXml = _package.GetXmlFromUri(StylesUri); - } else { - // create a new styles part and add to the package - ZipPackagePart part = _package.Package.CreatePart( - StylesUri, - "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", - _package.Compression); - // create the style sheet - - StringBuilder xml = new StringBuilder( - "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"); - xml.Append("<numFmts />"); - xml.Append( - "<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>"); - xml.Append( - "<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>"); - xml.Append( - "<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>"); - xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>"); - xml.Append( - "<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>"); - xml.Append( - "<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>"); - xml.Append("<dxfs count=\"0\" />"); - xml.Append("</styleSheet>"); - - _stylesXml = new(); - _stylesXml.LoadXml(xml.ToString()); - - //Save it to the package - StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write)); - - _stylesXml.Save(stream); - //stream.Close(); - - // create the relationship between the workbook and the new shared strings part - Part.CreateRelationship( - UriHelper.GetRelativeUri(WorkbookUri, StylesUri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/styles"); - } - } - return (_stylesXml); - } - set => _stylesXml = value; - } + private readonly XmlDocument _stylesXml; /// <summary> /// Package styles collection. Used internally to access style data. @@ -507,7 +445,7 @@ public ExcelStyles Styles { get { if (_styles == null) { - _styles = new(NameSpaceManager, StylesXml, this); + _styles = new(NameSpaceManager, _stylesXml, this); } return _styles; } @@ -527,14 +465,14 @@ } } - private readonly string CALC_MODE_PATH = "d:calcPr/@calcMode"; + private readonly string _calcModePath = "d:calcPr/@calcMode"; /// <summary> /// Calculation mode for the workbook. /// </summary> public ExcelCalcMode CalcMode { get { - string calcMode = GetXmlNodeString(CALC_MODE_PATH); + string calcMode = GetXmlNodeString(_calcModePath); switch (calcMode) { case "autoNoTable": return ExcelCalcMode.AutomaticNoTable; @@ -547,13 +485,13 @@ set { switch (value) { case ExcelCalcMode.AutomaticNoTable: - SetXmlNodeString(CALC_MODE_PATH, "autoNoTable"); + SetXmlNodeString(_calcModePath, "autoNoTable"); break; case ExcelCalcMode.Manual: - SetXmlNodeString(CALC_MODE_PATH, "manual"); + SetXmlNodeString(_calcModePath, "manual"); break; default: - SetXmlNodeString(CALC_MODE_PATH, "auto"); + SetXmlNodeString(_calcModePath, "auto"); break; } } @@ -570,44 +508,16 @@ set => SetXmlNodeBool(_fullCalcOnLoadPath, value); } - /// <summary> - /// Saves the workbook and all its components to the package. - /// For internal use only! - /// </summary> - internal void Save() // Workbook Save - { + internal void Save() { if (Worksheets.Count == 0) { throw new InvalidOperationException("The workbook must contain at least one worksheet"); } DeleteCalcChain(); - - const string vbaPartUri = "/xl/vbaProject.bin"; - if (!_package.Package.PartExists(new(vbaPartUri, UriKind.Relative))) { - if (Part.ContentType != ExcelPackage._contentTypeWorkbookDefault) { - Part.ContentType = ExcelPackage._contentTypeWorkbookDefault; - } - } else { - if (Part.ContentType != ExcelPackage._contentTypeWorkbookMacroEnabled) { - Part.ContentType = ExcelPackage._contentTypeWorkbookMacroEnabled; - } - } - UpdateDefinedNamesXml(); - // save the workbook - if (_workbookXml != null) { - _package.SavePart(WorkbookUri, _workbookXml); - } - - // save the properties of the workbook - if (_properties != null) { - _properties.Save(); - } - // save the style sheet Styles.UpdateXml(); - _package.SavePart(StylesUri, _stylesXml); // save all the open worksheets var isProtected = Protection.LockWindows || Protection.LockStructure; @@ -616,19 +526,16 @@ worksheet.View.WindowProtection = true; } worksheet.Save(); - worksheet.Part.SaveHandler = worksheet.SaveHandler; } - var part = _package.Package.CreatePart( + _package.Package.CreatePart( SharedStringsUri, ExcelPackage._contentTypeSharedString, - _package.Compression); - part.SaveHandler = SaveSharedStringHandler; + SaveSharedStringHandler); Part.CreateRelationship( UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), TargetMode.Internal, ExcelPackage._schemaRelationships + "/sharedStrings"); - //UpdateSharedStringsXml(); // Data validation ValidateDataValidations();
diff --git a/EPPlus/ExcelWorkbookView.cs b/EPPlus/ExcelWorkbookView.cs index 6772936..26a2430 100644 --- a/EPPlus/ExcelWorkbookView.cs +++ b/EPPlus/ExcelWorkbookView.cs
@@ -30,6 +30,7 @@ * Jan Källman License changed GPL-->LGPL 2011-12-27 *******************************************************************************/ +using System.Collections.Immutable; using System.Globalization; using System.Xml; @@ -39,6 +40,9 @@ /// Access to workbook view properties /// </summary> public class ExcelWorkbookView : XmlHelper { + protected override ImmutableArray<string> SchemaNodeOrder => + ExcelWorkbook.WorkbookSchemaNodeOrder; + /// <summary> /// Creates a new ExcelWorkbookView which provides access to all the /// view states of the worksheet. @@ -47,9 +51,7 @@ /// <param name="node"></param> /// <param name="wb"></param> internal ExcelWorkbookView(XmlNamespaceManager ns, XmlNode node, ExcelWorkbook wb) - : base(ns, node) { - SchemaNodeOrder = wb.SchemaNodeOrder; - } + : base(ns, node) {} private const string _leftPath = "d:bookViews/d:workbookView/@xWindow";
diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 33d2dd8..02fe010 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs
@@ -34,6 +34,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel; using System.Globalization; using System.IO; @@ -89,13 +90,12 @@ XmlNamespaceManager ns, ExcelPackage pck, ExcelWorkbook workbook, - string relId, - Uri uriWorksheet, - string sheetName, + Uri worksheetUri, + string name, int sheetId, int positionId, eWorkSheetHidden hidden) - : base(ns, pck, workbook, relId, uriWorksheet, sheetName, sheetId, positionId, hidden) {} + : base(ns, pck, workbook, worksheetUri, name, sheetId, positionId, hidden) {} } /// <summary> @@ -190,7 +190,7 @@ public class MergeCellsCollection : IEnumerable<string> { internal MergeCellsCollection() {} - internal CellStore<int> _cells = new(); + internal readonly CellStore<int> _cells = new(); private readonly List<string> _list = new(); internal List<string> List => _list; @@ -212,17 +212,13 @@ public string this[int index] => _list[index]; internal void Add(ExcelAddressBase address, bool doValidate) { - int ix = 0; - //Validate if (doValidate && Validate(address) == false) { throw (new ArgumentException("Can't merge and already merged range")); } - lock (this) { - ix = _list.Count; - _list.Add(address.Address); - SetIndex(address, ix); - } + var ix = _list.Count; + _list.Add(address.Address); + SetIndex(address, ix); } private bool Validate(ExcelAddressBase address) { @@ -329,143 +325,136 @@ } } - internal CellStore<object> _values; - internal CellStore<string> _types; - internal CellStore<int> _styles; - internal CellStore<object> _formulas; - internal FlagCellStore _flags; + internal readonly CellStore<object> _values = new(); + internal readonly CellStore<string> _types = new(); + internal readonly CellStore<int> _styles = new(); + internal readonly CellStore<object> _formulas = new(); + internal readonly FlagCellStore _flags = new(); internal CellStore<List<Token>> _formulaTokens; - internal CellStore<Uri> _hyperLinks; - internal CellStore<ExcelComment> _commentsStore; + internal readonly CellStore<Uri> _hyperLinks = new(); + internal readonly CellStore<ExcelComment> _commentsStore = new(); - internal Dictionary<int, Formulas> _sharedFormulas = new(); - internal int _minCol = ExcelPackage.MaxColumns; - internal int _maxCol = 0; + internal readonly Dictionary<int, Formulas> _sharedFormulas = new(); internal readonly ExcelPackage _package; private readonly ExcelWorkbook _workbook; - private readonly Uri _worksheetUri; - private string _name; - private readonly int _sheetID; - private int _positionID; - private readonly string _relationshipID; - private XmlDocument _worksheetXml; private ExcelWorksheetView _sheetView; - private ExcelHeaderFooter _headerFooter; + + internal static ImmutableArray<string> WorksheetSchemaNodeOrder = [ + "sheetPr", + "tabColor", + "outlinePr", + "pageSetUpPr", + "dimension", + "sheetViews", + "sheetFormatPr", + "cols", + "sheetData", + "sheetProtection", + "protectedRanges", + "scenarios", + "autoFilter", + "sortState", + "dataConsolidate", + "customSheetViews", + "customSheetViews", + "mergeCells", + "phoneticPr", + "conditionalFormatting", + "dataValidations", + "hyperlinks", + "printOptions", + "pageMargins", + "pageSetup", + "headerFooter", + "linePrint", + "rowBreaks", + "colBreaks", + "customProperties", + "cellWatches", + "ignoredErrors", + "smartTags", + "drawing", + "legacyDrawing", + "legacyDrawingHF", + "picture", + "oleObjects", + "activeXControls", + "webPublishItems", + "tableParts", + "extLst", + ]; + + protected override ImmutableArray<string> SchemaNodeOrder => WorksheetSchemaNodeOrder; internal ExcelWorksheet( XmlNamespaceManager ns, ExcelPackage excelPackage, ExcelWorkbook workbook, - string relId, - Uri uriWorksheet, - string sheetName, + Uri worksheetUri, + string name, int sheetId, int positionId, - eWorkSheetHidden hide) + eWorkSheetHidden hidden) : base(ns, null) { _workbook = workbook; - SchemaNodeOrder = new[] { - "sheetPr", - "tabColor", - "outlinePr", - "pageSetUpPr", - "dimension", - "sheetViews", - "sheetFormatPr", - "cols", - "sheetData", - "sheetProtection", - "protectedRanges", - "scenarios", - "autoFilter", - "sortState", - "dataConsolidate", - "customSheetViews", - "customSheetViews", - "mergeCells", - "phoneticPr", - "conditionalFormatting", - "dataValidations", - "hyperlinks", - "printOptions", - "pageMargins", - "pageSetup", - "headerFooter", - "linePrint", - "rowBreaks", - "colBreaks", - "customProperties", - "cellWatches", - "ignoredErrors", - "smartTags", - "drawing", - "legacyDrawing", - "legacyDrawingHF", - "picture", - "oleObjects", - "activeXControls", - "webPublishItems", - "tableParts", - "extLst", - }; _package = excelPackage; - _workbook = workbook; - _relationshipID = relId; - _worksheetUri = uriWorksheet; - _name = sheetName; - _sheetID = sheetId; - _positionID = positionId; - Hidden = hide; - - /**** Cellstore ****/ - _values = new(); - _types = new(); - _styles = new(); - _formulas = new(); - _flags = new(); - _commentsStore = new(); - _hyperLinks = new(); - _names = new(Workbook, this); - CreateXml(); - TopNode = _worksheetXml.DocumentElement; - } + Hidden = hidden; + Name = name; + PositionID = positionId; + SheetID = sheetId; - /// <summary> - /// The Uri to the worksheet within the package - /// </summary> - internal Uri WorksheetUri => (_worksheetUri); + Part = _package.Package.GetPart(worksheetUri); + Part.SaveHandler = SaveHandler; + + // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from an + // XmlTextReader to optimize speed. + using var stream = Part.GetStream(); + var xr = new XmlTextReader(stream); + xr.ProhibitDtd = true; + xr.WhitespaceHandling = WhitespaceHandling.None; + LoadColumns(xr); //columnXml + long start = stream.Position; + LoadCells(xr); + var nextElementLength = GetAttributeLength(xr); + long end = stream.Position - nextElementLength; + LoadMergeCells(xr); + LoadHyperLinks(xr); + LoadRowPageBreakes(xr); + LoadColPageBreakes(xr); + + // Then the rest of the XML is extracted and loaded into the WorksheetXml document. + stream.Seek(0, SeekOrigin.Begin); + var xml = GetWorkSheetXml(stream, start, end, out var encoding); + + //first char is invalid sometimes?? + if (xml[0] != '<') { + LoadXmlSafe(WorksheetXml, xml.Substring(1, xml.Length - 1), encoding); + } else { + LoadXmlSafe(WorksheetXml, xml, encoding); + } + ClearNodes(); + + TopNode = WorksheetXml.DocumentElement; + } /// <summary> /// The Zip.ZipPackagePart for the worksheet within the package /// </summary> - internal ZipPackagePart Part => (_package.Package.GetPart(WorksheetUri)); - - /// <summary> - /// The ID for the worksheet's relationship with the workbook in the package - /// </summary> - internal string RelationshipID => (_relationshipID); + internal ZipPackagePart Part { get; } /// <summary> /// The unique identifier for the worksheet. /// </summary> - internal int SheetID => (_sheetID); + internal int SheetID { get; } /// <summary> /// The position of the worksheet. /// </summary> - internal int PositionID { - get => (_positionID); - set => _positionID = value; - } - - /// <summary> - /// The index in the worksheets collection - /// </summary> - public int Index => (_positionID); + internal int PositionID { get; } /// <summary> /// Address for autofilter @@ -513,47 +502,9 @@ /// <summary> /// The worksheet's display name as it appears on the tab /// </summary> - public string Name { - get => (_name); - set { - if (value == _name) { - return; - } - value = _workbook.Worksheets.ValidateFixSheetName(value); - foreach (var ws in Workbook.Worksheets) { - if (ws.PositionID != PositionID - && ws.Name.Equals(value, StringComparison.InvariantCultureIgnoreCase)) { - throw (new ArgumentException("Worksheet name must be unique")); - } - } - _workbook.SetXmlNodeString( - string.Format("d:sheets/d:sheet[@sheetId={0}]/@name", _sheetID), - value); - ChangeNames(value); + public string Name { get; } - _name = value; - } - } - - private void ChangeNames(string value) { - //Renames name in this Worksheet; - foreach (var n in Workbook.Names) { - if (string.IsNullOrEmpty(n.NameFormula) && n.NameValue == null) { - n.ChangeWorksheet(_name, value); - } - } - foreach (var ws in Workbook.Worksheets) { - if (!(ws is ExcelChartsheet)) { - foreach (var n in ws.Names) { - if (string.IsNullOrEmpty(n.NameFormula) && n.NameValue == null) { - n.ChangeWorksheet(_name, value); - } - } - } - } - } - - internal ExcelNamedRangeCollection _names; + private readonly ExcelNamedRangeCollection _names; /// <summary> /// Provides access to named ranges @@ -571,7 +522,7 @@ public eWorkSheetHidden Hidden { get { string state = _workbook.GetXmlNodeString( - string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID)); + string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID)); if (state == "hidden") { return eWorkSheetHidden.Hidden; } @@ -582,13 +533,13 @@ } set { if (value == eWorkSheetHidden.Visible) { - _workbook.DeleteNode(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID)); + _workbook.DeleteNode(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID)); } else { string v; v = value.ToString(); v = v.Substring(0, 1).ToLower(CultureInfo.InvariantCulture) + v.Substring(1); _workbook.SetXmlNodeString( - string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID), + string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", SheetID), v); } } @@ -704,15 +655,11 @@ set => SetXmlNodeString(_codeModuleNamePath, value); } - internal void CodeNameChange(string value) { - CodeModuleName = value; - } - /// <summary> /// The XML document holding the worksheet data. /// All column, row, cell, pagebreak, merged cell and hyperlink-data are loaded into memory and removed from the document when loading the document. /// </summary> - public XmlDocument WorksheetXml => (_worksheetXml); + internal XmlDocument WorksheetXml { get; } = new(); internal ExcelVmlDrawingCommentCollection _vmlDrawings; @@ -745,7 +692,7 @@ } private void CreateVmlCollection() { - var vmlNode = _worksheetXml.DocumentElement.SelectSingleNode( + var vmlNode = WorksheetXml.DocumentElement.SelectSingleNode( "d:legacyDrawing/@r:id", NameSpaceManager); if (vmlNode == null) { @@ -761,41 +708,6 @@ } } - private void CreateXml() { - _worksheetXml = new(); - _worksheetXml.PreserveWhitespace = ExcelPackage._preserveWhitespace; - ZipPackagePart packPart = _package.Package.GetPart(WorksheetUri); - string xml = ""; - - // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from a xmlstream to optimize speed... - Stream stream = packPart.GetStream(); - - XmlTextReader xr = new XmlTextReader(stream); - xr.ProhibitDtd = true; - xr.WhitespaceHandling = WhitespaceHandling.None; - LoadColumns(xr); //columnXml - long start = stream.Position; - LoadCells(xr); - var nextElementLength = GetAttributeLength(xr); - long end = stream.Position - nextElementLength; - LoadMergeCells(xr); - LoadHyperLinks(xr); - LoadRowPageBreakes(xr); - LoadColPageBreakes(xr); - //...then the rest of the Xml is extracted and loaded into the WorksheetXml document. - stream.Seek(0, SeekOrigin.Begin); - xml = GetWorkSheetXml(stream, start, end, out var encoding); - - //first char is invalid sometimes?? - if (xml[0] != '<') { - LoadXmlSafe(_worksheetXml, xml.Substring(1, xml.Length - 1), encoding); - } else { - LoadXmlSafe(_worksheetXml, xml, encoding); - } - - ClearNodes(); - } - /// <summary> /// Get the lenth of the attributes /// Conditional formatting attributes can be extremly long som get length of the attributes to finetune position. @@ -850,20 +762,20 @@ } private void ClearNodes() { - if (_worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager) != null) { - _worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager).RemoveAll(); + if (WorksheetXml.SelectSingleNode("//d:cols", NameSpaceManager) != null) { + WorksheetXml.SelectSingleNode("//d:cols", NameSpaceManager).RemoveAll(); } - if (_worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager) != null) { - _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager).RemoveAll(); + if (WorksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager) != null) { + WorksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager).RemoveAll(); } - if (_worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager) != null) { - _worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager).RemoveAll(); + if (WorksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager) != null) { + WorksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager).RemoveAll(); } - if (_worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager) != null) { - _worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager).RemoveAll(); + if (WorksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager) != null) { + WorksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager).RemoveAll(); } - if (_worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager) != null) { - _worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll(); + if (WorksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager) != null) { + WorksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll(); } } @@ -1250,11 +1162,6 @@ break; } } - //if (cell != null) cellList.Add(cell); - - //_cells = new RangeCollection(cellList); - //_rows = new RangeCollection(rowList); - //_formulaCells = new RangeCollection(formulaList); } private bool DoAddRow(XmlTextReader xr) { @@ -1277,16 +1184,6 @@ } if (xr.NodeType == XmlNodeType.Element) { string address = xr.GetAttribute("ref"); - //int fromRow, fromCol, toRow, toCol; - //ExcelCellBase.GetRowColFromAddress(address, out fromRow, out fromCol, out toRow, out toCol); - //for (int row = fromRow; row <= toRow; row++) - //{ - // for (int col = fromCol; col <= toCol; col++) - // { - // _flags.SetFlagValue(row, col, true,CellFlags.Merged); - // } - //} - //_mergedCells.List.Add(address); _mergedCells.Add(new ExcelAddress(address), false); } } @@ -1475,45 +1372,6 @@ //} } - //private string GetSharedString(int stringID) - //{ - // string retValue = null; - // XmlNodeList stringNodes = xlPackage.Workbook.SharedStringsXml.SelectNodes(string.Format("//d:si", stringID), NameSpaceManager); - // XmlNode stringNode = stringNodes[stringID]; - // if (stringNode != null) - // retValue = stringNode.InnerText; - // return (retValue); - //} - - /// <summary> - /// A reference to the header and footer class which allows you to - /// set the header and footer for all odd, even and first pages of the worksheet - /// </summary> - /// <remarks> - /// To format the text you can use the following format - /// <list type="table"> - /// <listheader><term>Prefix</term><description>Description</description></listheader> - /// <item><term>&U</term><description>Underlined</description></item> - /// <item><term>&E</term><description>Double Underline</description></item> - /// <item><term>&K:xxxxxx</term><description>Color. ex &K:FF0000 for red</description></item> - /// <item><term>&"Font,Regular Bold Italic"</term><description>Changes the font. Regular or Bold or Italic or Bold Italic can be used. ex &"Arial,Bold Italic"</description></item> - /// <item><term>&nn</term><description>Change font size. nn is an integer. ex &24</description></item> - /// <item><term>&G</term><description>Placeholder for images. Images can not be added by the library, but its possible to use in a template.</description></item> - /// </list> - /// </remarks> - public ExcelHeaderFooter HeaderFooter { - get { - if (_headerFooter == null) { - XmlNode headerFooterNode = TopNode.SelectSingleNode("d:headerFooter", NameSpaceManager); - if (headerFooterNode == null) { - headerFooterNode = CreateNode("d:headerFooter"); - } - _headerFooter = new(NameSpaceManager, headerFooterNode, this); - } - return (_headerFooter); - } - } - ///// <summary> ///// Provides access to an individual cell within the worksheet. ///// </summary> @@ -1772,58 +1630,56 @@ "Can't insert. Rows will be shifted outside the boundries of the worksheet.")); } - lock (this) { - _values.Insert(rowFrom, 0, rows, 0); - _formulas.Insert(rowFrom, 0, rows, 0); - _styles.Insert(rowFrom, 0, rows, 0); - _types.Insert(rowFrom, 0, rows, 0); - _commentsStore.Insert(rowFrom, 0, rows, 0); - _hyperLinks.Insert(rowFrom, 0, rows, 0); - _flags.Insert(rowFrom, 0, rows, 0); + _values.Insert(rowFrom, 0, rows, 0); + _formulas.Insert(rowFrom, 0, rows, 0); + _styles.Insert(rowFrom, 0, rows, 0); + _types.Insert(rowFrom, 0, rows, 0); + _commentsStore.Insert(rowFrom, 0, rows, 0); + _hyperLinks.Insert(rowFrom, 0, rows, 0); + _flags.Insert(rowFrom, 0, rows, 0); - foreach (var f in _sharedFormulas.Values) { - if (f.StartRow >= rowFrom) { - f.StartRow += rows; - } - var a = new ExcelAddressBase(f.Address); - if (a._fromRow >= rowFrom) { - a._fromRow += rows; - a._toRow += rows; - } else if (a._toRow >= rowFrom) { - a._toRow += rows; - } - f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol); - f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, rows, 0, rowFrom, 0); + foreach (var f in _sharedFormulas.Values) { + if (f.StartRow >= rowFrom) { + f.StartRow += rows; } - var cse = new CellsStoreEnumerator<object>(_formulas); - while (cse.Next()) { - if (cse.Value is string) { - cse.Value = ExcelCellBase.UpdateFormulaReferences( - cse.Value.ToString(), - rows, - 0, - rowFrom, - 0); - } + var a = new ExcelAddressBase(f.Address); + if (a._fromRow >= rowFrom) { + a._fromRow += rows; + a._toRow += rows; + } else if (a._toRow >= rowFrom) { + a._toRow += rows; } - - FixMergedCellsRow(rowFrom, rows, false); - if (copyStylesFromRow > 0) { - var cseS = new CellsStoreEnumerator<int>( - _styles, - copyStylesFromRow, + f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol); + f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, rows, 0, rowFrom, 0); + } + var cse = new CellsStoreEnumerator<object>(_formulas); + while (cse.Next()) { + if (cse.Value is string) { + cse.Value = ExcelCellBase.UpdateFormulaReferences( + cse.Value.ToString(), + rows, 0, - copyStylesFromRow, - ExcelPackage.MaxColumns); //Fixes issue 15068 , 15090 - while (cseS.Next()) { - for (var r = 0; r < rows; r++) { - _styles.SetValue(rowFrom + r, cseS.Column, cseS.Value); - } + rowFrom, + 0); + } + } + + FixMergedCellsRow(rowFrom, rows, false); + if (copyStylesFromRow > 0) { + var cseS = new CellsStoreEnumerator<int>( + _styles, + copyStylesFromRow, + 0, + copyStylesFromRow, + ExcelPackage.MaxColumns); //Fixes issue 15068 , 15090 + while (cseS.Next()) { + for (var r = 0; r < rows; r++) { + _styles.SetValue(rowFrom + r, cseS.Column, cseS.Value); } } - foreach (var tbl in Tables) { - tbl.Address = tbl.Address.AddRow(rowFrom, rows); - } + } + foreach (var tbl in Tables) { + tbl.Address = tbl.Address.AddRow(rowFrom, rows); } } @@ -1859,87 +1715,85 @@ "Can't insert. Columns will be shifted outside the boundries of the worksheet.")); } - lock (this) { - _values.Insert(0, columnFrom, 0, columns); - _formulas.Insert(0, columnFrom, 0, columns); - _styles.Insert(0, columnFrom, 0, columns); - _types.Insert(0, columnFrom, 0, columns); - _commentsStore.Insert(0, columnFrom, 0, columns); - _hyperLinks.Insert(0, columnFrom, 0, columns); - _flags.Insert(0, columnFrom, 0, columns); + _values.Insert(0, columnFrom, 0, columns); + _formulas.Insert(0, columnFrom, 0, columns); + _styles.Insert(0, columnFrom, 0, columns); + _types.Insert(0, columnFrom, 0, columns); + _commentsStore.Insert(0, columnFrom, 0, columns); + _hyperLinks.Insert(0, columnFrom, 0, columns); + _flags.Insert(0, columnFrom, 0, columns); - foreach (var f in _sharedFormulas.Values) { - if (f.StartCol >= columnFrom) { - f.StartCol += columns; + foreach (var f in _sharedFormulas.Values) { + if (f.StartCol >= columnFrom) { + f.StartCol += columns; + } + var a = new ExcelAddressBase(f.Address); + if (a._fromCol >= columnFrom) { + a._fromCol += columns; + a._toCol += columns; + } else if (a._toCol >= columnFrom) { + a._toCol += columns; + } + f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol); + f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, 0, columns, 0, columnFrom); + } + + var cse = new CellsStoreEnumerator<object>(_formulas); + while (cse.Next()) { + if (cse.Value is string) { + cse.Value = ExcelCellBase.UpdateFormulaReferences( + cse.Value.ToString(), + 0, + columns, + 0, + columnFrom); + } + } + + FixMergedCellsColumn(columnFrom, columns, false); + + var csec = new CellsStoreEnumerator<object>(_values, 0, 1, 0, ExcelPackage.MaxColumns); + var lst = new List<ExcelColumn>(); + foreach (var col in csec) { + if (col is ExcelColumn column) { + lst.Add(column); + } + } + + for (int i = lst.Count - 1; i >= 0; i--) { + var c = lst[i]; + if (c._columnMin >= columnFrom) { + if (c._columnMin + columns <= ExcelPackage.MaxColumns) { + c._columnMin += columns; + } else { + c._columnMin = ExcelPackage.MaxColumns; } - var a = new ExcelAddressBase(f.Address); - if (a._fromCol >= columnFrom) { - a._fromCol += columns; - a._toCol += columns; - } else if (a._toCol >= columnFrom) { - a._toCol += columns; + + if (c._columnMax + columns <= ExcelPackage.MaxColumns) { + c._columnMax += columns; + } else { + c._columnMax = ExcelPackage.MaxColumns; } - f.Address = ExcelCellBase.GetAddress(a._fromRow, a._fromCol, a._toRow, a._toCol); - f.Formula = ExcelCellBase.UpdateFormulaReferences(f.Formula, 0, columns, 0, columnFrom); + } else if (c._columnMax >= columnFrom) { + var cc = c._columnMax - columnFrom; + c._columnMax = columnFrom - 1; + CopyColumn(c, columnFrom + columns, columnFrom + columns + cc); + } + } + + if (copyStylesFromColumn > 0) { + for (var c = 0; c < columns; c++) { + var col = Column(columnFrom + c); + col.StyleID = Column(copyStylesFromColumn).StyleID; + } + } + //Adjust tables + foreach (var tbl in Tables) { + if (columnFrom > tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) { + InsertTableColumns(columnFrom, columns, tbl); } - var cse = new CellsStoreEnumerator<object>(_formulas); - while (cse.Next()) { - if (cse.Value is string) { - cse.Value = ExcelCellBase.UpdateFormulaReferences( - cse.Value.ToString(), - 0, - columns, - 0, - columnFrom); - } - } - - FixMergedCellsColumn(columnFrom, columns, false); - - var csec = new CellsStoreEnumerator<object>(_values, 0, 1, 0, ExcelPackage.MaxColumns); - var lst = new List<ExcelColumn>(); - foreach (var col in csec) { - if (col is ExcelColumn column) { - lst.Add(column); - } - } - - for (int i = lst.Count - 1; i >= 0; i--) { - var c = lst[i]; - if (c._columnMin >= columnFrom) { - if (c._columnMin + columns <= ExcelPackage.MaxColumns) { - c._columnMin += columns; - } else { - c._columnMin = ExcelPackage.MaxColumns; - } - - if (c._columnMax + columns <= ExcelPackage.MaxColumns) { - c._columnMax += columns; - } else { - c._columnMax = ExcelPackage.MaxColumns; - } - } else if (c._columnMax >= columnFrom) { - var cc = c._columnMax - columnFrom; - c._columnMax = columnFrom - 1; - CopyColumn(c, columnFrom + columns, columnFrom + columns + cc); - } - } - - if (copyStylesFromColumn > 0) { - for (var c = 0; c < columns; c++) { - var col = Column(columnFrom + c); - col.StyleID = Column(copyStylesFromColumn).StyleID; - } - } - //Adjust tables - foreach (var tbl in Tables) { - if (columnFrom > tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) { - InsertTableColumns(columnFrom, columns, tbl); - } - - tbl.Address = tbl.Address.AddColumn(columnFrom, columns); - } + tbl.Address = tbl.Address.AddColumn(columnFrom, columns); } } @@ -2064,21 +1918,19 @@ "Row out of range. Spans from 1 to " + ExcelPackage.MaxRows.ToString(CultureInfo.InvariantCulture))); } - lock (this) { - _values.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _types.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _formulas.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _styles.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _flags.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _commentsStore.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - _hyperLinks.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _values.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _types.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _formulas.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _styles.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _flags.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _commentsStore.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); + _hyperLinks.Delete(rowFrom, 0, rows, ExcelPackage.MaxColumns); - AdjustFormulasRow(rowFrom, rows); - FixMergedCellsRow(rowFrom, rows, true); + AdjustFormulasRow(rowFrom, rows); + FixMergedCellsRow(rowFrom, rows, true); - foreach (var tbl in Tables) { - tbl.Address = tbl.Address.DeleteRow(rowFrom, rows); - } + foreach (var tbl in Tables) { + tbl.Address = tbl.Address.DeleteRow(rowFrom, rows); } } @@ -2101,59 +1953,52 @@ "Column out of range. Spans from 1 to " + ExcelPackage.MaxColumns.ToString(CultureInfo.InvariantCulture))); } - lock (this) { - var col = _values.GetValue(0, columnFrom) as ExcelColumn; - if (col == null) { - var r = 0; - var c = columnFrom; - if (_values.PrevCell(ref r, ref c)) { - col = _values.GetValue(0, c) as ExcelColumn; - if (col._columnMax >= columnFrom) { - col.ColumnMax = columnFrom - 1; - } + var col = _values.GetValue(0, columnFrom) as ExcelColumn; + if (col == null) { + var r = 0; + var c = columnFrom; + if (_values.PrevCell(ref r, ref c)) { + col = _values.GetValue(0, c) as ExcelColumn; + if (col._columnMax >= columnFrom) { + col.ColumnMax = columnFrom - 1; } } + } - _values.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _types.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _formulas.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _styles.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _flags.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _commentsStore.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - _hyperLinks.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _values.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _types.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _formulas.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _styles.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _flags.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _commentsStore.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); + _hyperLinks.Delete(0, columnFrom, ExcelPackage.MaxRows, columns); - AdjustFormulasColumn(columnFrom, columns); - FixMergedCellsColumn(columnFrom, columns, true); + AdjustFormulasColumn(columnFrom, columns); + FixMergedCellsColumn(columnFrom, columns, true); - var csec = new CellsStoreEnumerator<object>( - _values, - 0, - columnFrom, - 0, - ExcelPackage.MaxColumns); - foreach (var column in csec) { - if (column is ExcelColumn excelColumn) { - if (excelColumn._columnMin >= columnFrom) { - excelColumn._columnMin -= columns; - excelColumn._columnMax -= columns; - } + var csec = new CellsStoreEnumerator<object>(_values, 0, columnFrom, 0, ExcelPackage.MaxColumns); + foreach (var column in csec) { + if (column is ExcelColumn excelColumn) { + if (excelColumn._columnMin >= columnFrom) { + excelColumn._columnMin -= columns; + excelColumn._columnMax -= columns; } } + } - foreach (var tbl in Tables) { - if (columnFrom >= tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) { - var node = tbl.Columns[0].TopNode.ParentNode; - var ix = columnFrom - tbl.Address.Start.Column; - for (int i = 0; i < columns; i++) { - if (node.ChildNodes.Count > ix) { - node.RemoveChild(node.ChildNodes[ix]); - } + foreach (var tbl in Tables) { + if (columnFrom >= tbl.Address.Start.Column && columnFrom <= tbl.Address.End.Column) { + var node = tbl.Columns[0].TopNode.ParentNode; + var ix = columnFrom - tbl.Address.Start.Column; + for (int i = 0; i < columns; i++) { + if (node.ChildNodes.Count > ix) { + node.RemoveChild(node.ChildNodes[ix]); } - tbl._cols = new(tbl); } - - tbl.Address = tbl.Address.DeleteColumn(columnFrom, columns); + tbl._cols = new(tbl); } + + tbl.Address = tbl.Address.DeleteColumn(columnFrom, columns); } } @@ -2175,7 +2020,6 @@ foreach (var ix in delSf) { _sharedFormulas.Remove(ix); } - delSf = null; var cse = new CellsStoreEnumerator<object>( _formulas, 1, @@ -2212,7 +2056,6 @@ foreach (var ix in delSf) { _sharedFormulas.Remove(ix); } - delSf = null; var cse = new CellsStoreEnumerator<object>( _formulas, 1, @@ -2406,32 +2249,22 @@ internal void Save() { DeletePrinterSettings(); - - if (_worksheetXml != null) { - if (!(this is ExcelChartsheet)) { - // save the header & footer (if defined) - if (_headerFooter != null) { - HeaderFooter.Save(); - } - - var d = Dimension; - if (d == null) { - DeleteAllNode("d:dimension/@ref"); - } else { - SetXmlNodeString("d:dimension/@ref", d.Address); - } - - SaveComments(); - SaveTables(); - SavePivotTables(); + if (!(this is ExcelChartsheet)) { + var d = Dimension; + if (d == null) { + DeleteAllNode("d:dimension/@ref"); + } else { + SetXmlNodeString("d:dimension/@ref", d.Address); } + SaveTables(); + SavePivotTables(); } } internal void SaveHandler(StreamWriter streamWriter) { //Create the nodes if they do not exist. if (this is ExcelChartsheet) { - streamWriter.Write(_worksheetXml.OuterXml); + streamWriter.Write(WorksheetXml.OuterXml); } else { CreateNode("d:cols"); CreateNode("d:sheetData"); @@ -2441,7 +2274,7 @@ CreateNode("d:colBreaks"); //StreamWriter sw=new StreamWriter(Part.GetStream(FileMode.Create, FileAccess.Write)); - var xml = _worksheetXml.OuterXml; + var xml = WorksheetXml.OuterXml; int colStart = 0, colEnd = 0; GetBlockPos(xml, "cols", ref colStart, ref colEnd); @@ -2512,59 +2345,6 @@ } } - private void SaveComments() { - if (_comments != null) { - if (_comments.Count == 0) { - if (_comments.Uri != null) { - Part.DeleteRelationship(_comments.RelId); - _package.Package.DeletePart(_comments.Uri); - } - RemoveLegacyDrawingRel(VmlDrawingsComments.RelId); - } else { - if (_comments.Uri == null) { - _comments.Uri = new(string.Format("/xl/comments{0}.xml", SheetID), UriKind.Relative); - } - if (_comments.Part == null) { - _comments.Part = _package.Package.CreatePart( - _comments.Uri, - "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", - _package.Compression); - var rel = Part.CreateRelationship( - UriHelper.GetRelativeUri(WorksheetUri, _comments.Uri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/comments"); - } - _comments.CommentXml.Save(_comments.Part.GetStream(FileMode.Create)); - } - } - - if (_vmlDrawings != null) { - if (_vmlDrawings.Count == 0) { - if (_vmlDrawings.Uri != null) { - Part.DeleteRelationship(_vmlDrawings.RelId); - _package.Package.DeletePart(_vmlDrawings.Uri); - } - } else { - if (_vmlDrawings.Uri == null) { - _vmlDrawings.Uri = GetNewUri(_package.Package, "/xl/drawings/vmlDrawing{0}.vml"); - } - if (_vmlDrawings.Part == null) { - _vmlDrawings.Part = _package.Package.CreatePart( - _vmlDrawings.Uri, - "application/vnd.openxmlformats-officedocument.vmlDrawing", - _package.Compression); - var rel = Part.CreateRelationship( - UriHelper.GetRelativeUri(WorksheetUri, _vmlDrawings.Uri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/vmlDrawing"); - SetXmlNodeString("d:legacyDrawing/@r:id", rel.Id); - _vmlDrawings.RelId = rel.Id; - } - _vmlDrawings.VmlDrawingXml.Save(_vmlDrawings.Part.GetStream()); - } - } - } - /// <summary> /// Save all table data /// </summary> @@ -2614,30 +2394,6 @@ colNum++; } } - if (tbl.Part == null) { - tbl.TableUri = GetNewUri(_package.Package, "/xl/tables/table{0}.xml", tbl.Id); - tbl.Part = _package.Package.CreatePart( - tbl.TableUri, - "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", - Workbook._package.Compression); - var stream = tbl.Part.GetStream(FileMode.Create); - tbl.TableXml.Save(stream); - var rel = Part.CreateRelationship( - UriHelper.GetRelativeUri(WorksheetUri, tbl.TableUri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/table"); - tbl.RelationshipID = rel.Id; - - CreateNode("d:tableParts"); - XmlNode tbls = TopNode.SelectSingleNode("d:tableParts", NameSpaceManager); - - var tblNode = tbls.OwnerDocument.CreateElement("tablePart", ExcelPackage._schemaMain); - tbls.AppendChild(tblNode); - tblNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id); - } else { - var stream = tbl.Part.GetStream(FileMode.Create); - tbl.TableXml.Save(stream); - } } } @@ -2775,9 +2531,6 @@ } } } - pt.PivotTableXml.Save(pt.Part.GetStream(FileMode.Create)); - pt.CacheDefinition.CacheDefinitionXml.Save( - pt.CacheDefinition.Part.GetStream(FileMode.Create)); } } @@ -2896,7 +2649,6 @@ int row = -1; - StringBuilder sbXml = new StringBuilder(); var ss = _workbook._sharedStrings; var styles = _workbook.Styles; var cache = new StringBuilder(); @@ -3190,16 +2942,13 @@ ? "" : "tooltip=\"" + SecurityElement.Escape(link.ToolTip) + "\" "); } else if (uri != null) { - string id; Uri hyp; if (uri is ExcelHyperLink hyperLink) { hyp = hyperLink.OriginalUri; } else { hyp = uri; } - if (hyps.ContainsKey(hyp.OriginalString)) { - id = hyps[hyp.OriginalString]; - } else { + if (!hyps.ContainsKey(hyp.OriginalString)) { var relationship = Part.CreateRelationship( hyp, TargetMode.External, @@ -3221,9 +2970,7 @@ ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id); } - id = relationship.Id; } - //cell.HyperLinkRId = id; } } if (!first) { @@ -3232,24 +2979,6 @@ } /// <summary> - /// Create the hyperlinks node in the XML - /// </summary> - /// <returns></returns> - private XmlNode CreateHyperLinkCollection() { - XmlElement hl = _worksheetXml.CreateElement("hyperlinks", ExcelPackage._schemaMain); - XmlNode prevNode = _worksheetXml.SelectSingleNode( - "//d:conditionalFormatting", - NameSpaceManager); - if (prevNode == null) { - prevNode = _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager); - if (prevNode == null) { - prevNode = _worksheetXml.SelectSingleNode("//d:sheetData", NameSpaceManager); - } - } - return _worksheetXml.DocumentElement.InsertAfter(hl, prevNode); - } - - /// <summary> /// Dimension address for the worksheet. /// Top left cell to Bottom right. /// If the worksheet has no cells, null is returned @@ -3274,7 +3003,7 @@ public ExcelSheetProtection Protection { get { if (_protection == null) { - _protection = new(NameSpaceManager, TopNode, this); + _protection = new(NameSpaceManager, TopNode); } return _protection; } @@ -3285,7 +3014,7 @@ public ExcelProtectedRangeCollection ProtectedRanges { get { if (_protectedRanges == null) { - _protectedRanges = new(NameSpaceManager, TopNode, this); + _protectedRanges = new(NameSpaceManager, TopNode); } return _protectedRanges; } @@ -3399,19 +3128,6 @@ return i; } - internal void SetHfLegacyDrawingRel(string relId) { - SetXmlNodeString("d:legacyDrawingHF/@r:id", relId); - } - - internal void RemoveLegacyDrawingRel(string relId) { - var n = WorksheetXml.DocumentElement.SelectSingleNode( - string.Format("d:legacyDrawing[@r:id=\"{0}\"]", relId), - NameSpaceManager); - if (n != null) { - n.ParentNode.RemoveChild(n); - } - } - internal void UpdateCellsWithDate1904Setting() { var cse = new CellsStoreEnumerator<object>(_values); var offset = Workbook.Date1904 ? -ExcelWorkbook._date1904Offset : ExcelWorkbook._date1904Offset;
diff --git a/EPPlus/ExcelWorksheetView.cs b/EPPlus/ExcelWorksheetView.cs index bd132e5..758ec6e 100644 --- a/EPPlus/ExcelWorksheetView.cs +++ b/EPPlus/ExcelWorksheetView.cs
@@ -31,6 +31,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Xml; namespace OfficeOpenXml; @@ -127,6 +128,13 @@ private readonly ExcelWorksheet _worksheet; + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "sheetViews", + "sheetView", + "pane", + "selection", + ]; + /// <summary> /// Creates a new ExcelWorksheetView which provides access to all the view states of the worksheet. /// </summary> @@ -136,7 +144,6 @@ internal ExcelWorksheetView(XmlNamespaceManager ns, XmlNode node, ExcelWorksheet xlWorksheet) : base(ns, node) { _worksheet = xlWorksheet; - SchemaNodeOrder = new[] { "sheetViews", "sheetView", "pane", "selection" }; Panes = LoadPanes(); }
diff --git a/EPPlus/ExcelWorksheets.cs b/EPPlus/ExcelWorksheets.cs index 1ed69a2..a0e4ebb 100644 --- a/EPPlus/ExcelWorksheets.cs +++ b/EPPlus/ExcelWorksheets.cs
@@ -33,10 +33,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Text.RegularExpressions; using System.Xml; -using OfficeOpenXml.Packaging; using OfficeOpenXml.Utils; namespace OfficeOpenXml; @@ -47,7 +45,7 @@ public class ExcelWorksheets : XmlHelper, IEnumerable<ExcelWorksheet> { private readonly ExcelPackage _pck; private readonly ExcelWorkbook _workbook; - private Dictionary<int, ExcelWorksheet> _worksheets; + private Dictionary<int, ExcelWorksheet> _worksheets = new(); private readonly XmlNamespaceManager _namespaceManager; internal ExcelWorksheets( @@ -59,7 +57,6 @@ _pck = pck; _workbook = workbook; _namespaceManager = nsm; - _worksheets = new(); int positionId = 1; foreach (XmlNode sheetNode in topNode.ChildNodes) { @@ -89,7 +86,6 @@ _namespaceManager, pck, _workbook, - relId, uriWorksheet, name, sheetId, @@ -102,7 +98,6 @@ _namespaceManager, pck, _workbook, - relId, uriWorksheet, name, sheetId, @@ -152,65 +147,42 @@ /// </summary> /// <param name="name">The name of the workbook</param> public ExcelWorksheet Add(string name) { - int sheetId; - Uri uriWorksheet; - lock (_worksheets) { - name = ValidateFixSheetName(name); - if (GetByName(name) != null) { - throw (new InvalidOperationException(_errDupWorksheet + " : " + name)); - } - GetSheetUri(ref name, out sheetId, out uriWorksheet, false); - ZipPackagePart worksheetPart = _pck.Package.CreatePart( - uriWorksheet, - _worksheetContentType, - _pck.Compression); - - //Create the new, empty worksheet and save it to the package - StreamWriter streamWorksheet = new StreamWriter( - worksheetPart.GetStream(FileMode.Create, FileAccess.Write)); - XmlDocument worksheetXml = CreateNewWorksheet(false); - worksheetXml.Save(streamWorksheet); - - string rel = CreateWorkbookRel(name, sheetId, uriWorksheet, false); - - int positionId = _worksheets.Count + 1; - ExcelWorksheet worksheet; - - { - worksheet = new( - _namespaceManager, - _pck, - _workbook, - rel, - uriWorksheet, - name, - sheetId, - positionId, - eWorkSheetHidden.Visible); - } - - _worksheets.Add(positionId, worksheet); - return worksheet; + name = ValidateFixSheetName(name); + if (GetByName(name) != null) { + throw (new InvalidOperationException(_errDupWorksheet + " : " + name)); } - } + GetSheetUri(ref name, out var sheetId, out var uriWorksheet, false); - private string CreateWorkbookRel(string name, int sheetId, Uri uriWorksheet, bool isChart) { - //Create the relationship between the workbook and the new worksheet - var rel = _workbook.Part.CreateRelationship( - UriHelper.GetRelativeUri(ExcelWorkbook.WorkbookUri, uriWorksheet), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/" + (isChart ? "chartsheet" : "worksheet")); + // Create the new worksheet + var rel = _pck.CreateXmlDocument( + uriWorksheet, + _worksheetContentType, + ExcelPackage._schemaRelationships + "/worksheet", + CreateNewWorksheet(false)); - //Create the new sheet node + // Add worksheet to the workbook XmlElement worksheetNode = _workbook.WorkbookXml.CreateElement( "sheet", ExcelPackage._schemaMain); worksheetNode.SetAttribute("name", name); worksheetNode.SetAttribute("sheetId", sheetId.ToString()); worksheetNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id); - TopNode.AppendChild(worksheetNode); - return rel.Id; + + int positionId = _worksheets.Count + 1; + + ExcelWorksheet worksheet = new( + _namespaceManager, + _pck, + _workbook, + uriWorksheet, + name, + sheetId, + positionId, + eWorkSheetHidden.Visible); + + _worksheets.Add(positionId, worksheet); + return worksheet; } private void GetSheetUri(ref string name, out int sheetId, out Uri uriWorksheet, bool isChart) { @@ -361,181 +333,4 @@ } return (xlWorksheet); } - - /// <summary> - /// Moves the source worksheet to the position before the target worksheet - /// </summary> - /// <param name="sourceName">The name of the source worksheet</param> - /// <param name="targetName">The name of the target worksheet</param> - public void MoveBefore(string sourceName, string targetName) { - Move(sourceName, targetName, false); - } - - /// <summary> - /// Moves the source worksheet to the position before the target worksheet - /// </summary> - /// <param name="sourcePositionId">The id of the source worksheet</param> - /// <param name="targetPositionId">The id of the target worksheet</param> - public void MoveBefore(int sourcePositionId, int targetPositionId) { - Move(sourcePositionId, targetPositionId, false); - } - - /// <summary> - /// Moves the source worksheet to the position after the target worksheet - /// </summary> - /// <param name="sourceName">The name of the source worksheet</param> - /// <param name="targetName">The name of the target worksheet</param> - public void MoveAfter(string sourceName, string targetName) { - Move(sourceName, targetName, true); - } - - /// <summary> - /// Moves the source worksheet to the position after the target worksheet - /// </summary> - /// <param name="sourcePositionId">The id of the source worksheet</param> - /// <param name="targetPositionId">The id of the target worksheet</param> - public void MoveAfter(int sourcePositionId, int targetPositionId) { - Move(sourcePositionId, targetPositionId, true); - } - - /// <summary> - /// - /// </summary> - /// <param name="sourceName"></param> - public void MoveToStart(string sourceName) { - var sourceSheet = this[sourceName]; - if (sourceSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet to move '{0}'", - sourceName)); - } - Move(sourceSheet.PositionID, 1, false); - } - - /// <summary> - /// - /// </summary> - /// <param name="sourcePositionId"></param> - public void MoveToStart(int sourcePositionId) { - Move(sourcePositionId, 1, false); - } - - /// <summary> - /// - /// </summary> - /// <param name="sourceName"></param> - public void MoveToEnd(string sourceName) { - var sourceSheet = this[sourceName]; - if (sourceSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet to move '{0}'", - sourceName)); - } - Move(sourceSheet.PositionID, _worksheets.Count, true); - } - - /// <summary> - /// - /// </summary> - /// <param name="sourcePositionId"></param> - public void MoveToEnd(int sourcePositionId) { - Move(sourcePositionId, _worksheets.Count, true); - } - - private void Move(string sourceName, string targetName, bool placeAfter) { - var sourceSheet = this[sourceName]; - if (sourceSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet to move '{0}'", - sourceName)); - } - var targetSheet = this[targetName]; - if (targetSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet to move '{0}'", - targetName)); - } - Move(sourceSheet.PositionID, targetSheet.PositionID, placeAfter); - } - - private void Move(int sourcePositionId, int targetPositionId, bool placeAfter) { - // Bugfix: if source and target are the same worksheet the following code will create a duplicate - // which will cause a corrupt workbook. /swmal 2014-05-10 - if (sourcePositionId == targetPositionId) { - return; - } - - lock (_worksheets) { - var sourceSheet = this[sourcePositionId]; - if (sourceSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet at position '{0}'", - sourcePositionId)); - } - var targetSheet = this[targetPositionId]; - if (targetSheet == null) { - throw new( - string.Format( - "Move worksheet error: Could not find worksheet at position '{0}'", - targetPositionId)); - } - if (sourcePositionId == targetPositionId && _worksheets.Count < 2) { - return; //--- no reason to attempt to re-arrange a single item with itself - } - - var index = 1; - var newOrder = new Dictionary<int, ExcelWorksheet>(); - foreach (var entry in _worksheets) { - if (entry.Key == targetPositionId) { - if (!placeAfter) { - sourceSheet.PositionID = index; - newOrder.Add(index++, sourceSheet); - } - - entry.Value.PositionID = index; - newOrder.Add(index++, entry.Value); - - if (placeAfter) { - sourceSheet.PositionID = index; - newOrder.Add(index++, sourceSheet); - } - } else if (entry.Key == sourcePositionId) { - //--- do nothing - } else { - entry.Value.PositionID = index; - newOrder.Add(index++, entry.Value); - } - } - _worksheets = newOrder; - - MoveSheetXmlNode(sourceSheet, targetSheet, placeAfter); - } - } - - private void MoveSheetXmlNode( - ExcelWorksheet sourceSheet, - ExcelWorksheet targetSheet, - bool placeAfter) { - lock (TopNode.OwnerDocument) { - var sourceNode = TopNode.SelectSingleNode( - string.Format("d:sheet[@sheetId = '{0}']", sourceSheet.SheetID), - _namespaceManager); - var targetNode = TopNode.SelectSingleNode( - string.Format("d:sheet[@sheetId = '{0}']", targetSheet.SheetID), - _namespaceManager); - if (sourceNode == null || targetNode == null) { - throw new("Source SheetId and Target SheetId must be valid"); - } - if (placeAfter) { - TopNode.InsertAfter(sourceNode, targetNode); - } else { - TopNode.InsertBefore(sourceNode, targetNode); - } - } - } } // end class Worksheets
diff --git a/EPPlus/FormulaParsing/CalculateExtentions.cs b/EPPlus/FormulaParsing/CalculateExtentions.cs index 8d8792b..afeb3ff 100644 --- a/EPPlus/FormulaParsing/CalculateExtentions.cs +++ b/EPPlus/FormulaParsing/CalculateExtentions.cs
@@ -30,13 +30,9 @@ *******************************************************************************/ using System; -using System.Collections.Generic; -using System.IO; -using System.Text; using OfficeOpenXml.FormulaParsing; using OfficeOpenXml.FormulaParsing.Excel.Functions; using OfficeOpenXml.FormulaParsing.Exceptions; -using OfficeOpenXml.FormulaParsing.LexicalAnalysis; namespace OfficeOpenXml;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs b/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs index 86f9d5c..0a518f9 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/ArgumentParsers.cs
@@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Jan Källman +/* Copyright (C) 2011 Jan Källman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,7 +30,6 @@ namespace OfficeOpenXml.FormulaParsing.Excel.Functions; public class ArgumentParsers { - private static readonly object _syncRoot = new(); private readonly Dictionary<DataType, ArgumentParser> _parsers = new(); private readonly ArgumentParserFactory _parserFactory; @@ -44,10 +43,8 @@ public ArgumentParser GetParser(DataType dataType) { if (!_parsers.ContainsKey(dataType)) { - lock (_syncRoot) { - if (!_parsers.ContainsKey(dataType)) { - _parsers.Add(dataType, _parserFactory.CreateArgumentParser(dataType)); - } + if (!_parsers.ContainsKey(dataType)) { + _parsers.Add(dataType, _parserFactory.CreateArgumentParser(dataType)); } } return _parsers[dataType];
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs b/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs index 0316950..3af4c77 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Database/DcountA.cs
@@ -43,7 +43,7 @@ ValidateArguments(arguments, 2); var dbAddress = arguments.ElementAt(0).ValueAsRangeInfo.Address.Address; string field = null; - string criteriaRange = null; + string criteriaRange; if (arguments.Count() == 2) { criteriaRange = arguments.ElementAt(1).ValueAsRangeInfo.Address.Address; } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs index fbac43c..47143ef 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/DateParsingFunction.cs
@@ -5,7 +5,7 @@ public abstract class DateParsingFunction : ExcelFunction { protected System.DateTime ParseDate(IEnumerable<FunctionArgument> arguments, object dateObj) { - System.DateTime date = System.DateTime.MinValue; + System.DateTime date; if (dateObj is string) { date = System.DateTime.Parse(dateObj.ToString(), CultureInfo.InvariantCulture); } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs index 57426d0..a93b354 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/Minute.cs
@@ -35,7 +35,7 @@ ParsingContext context) { ValidateArguments(arguments, 1); var dateObj = arguments.ElementAt(0).Value; - System.DateTime date = System.DateTime.MinValue; + System.DateTime date; if (dateObj is string) { date = System.DateTime.Parse(dateObj.ToString()); } else {
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs index 879fc64..4b2ef4a 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeBaseFunction.cs
@@ -28,11 +28,7 @@ namespace OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; public abstract class TimeBaseFunction : ExcelFunction { - public TimeBaseFunction() { - TimeStringParser = new(); - } - - protected TimeStringParser TimeStringParser { get; private set; } + protected TimeStringParser TimeStringParser { get; private set; } = new(); protected double SerialNumber { get; private set; }
diff --git a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs index 533528d..0c403b6 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/DateTime/TimeStringParser.cs
@@ -71,8 +71,7 @@ } private double Parse12HourTimeString(string input) { - string dayPart = string.Empty; - dayPart = input.Substring(input.Length - 2, 2); + var dayPart = input.Substring(input.Length - 2, 2); GetValuesFromString(input, out var hour, out var minute, out var second); if (dayPart == "PM") { hour += 12;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs index 0423f98..5568a25 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Math/AverageIf.cs
@@ -76,7 +76,7 @@ ThrowExcelErrorValueExceptionIf( () => criteria == null || criteria.ToString().Length > 255, eErrorType.Value); - var retVal = 0d; + double retVal; if (arguments.Count() > 2) { var secondArg = arguments.ElementAt(2); var lookupRange = secondArg.Value as IEnumerable<FunctionArgument>;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs index cc82beb..17d8586 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Math/Subtotal.cs
@@ -81,8 +81,7 @@ return CreateResult(0d, DataType.Decimal); } var actualArgs = arguments.Skip(1); - ExcelFunction function = null; - function = GetFunctionByCalcType(funcNum); + var function = GetFunctionByCalcType(funcNum); var compileResult = function.Execute(actualArgs, context); compileResult.IsResultOfSubtotal = true; return compileResult;
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs index 232d460..8d8d61b 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs
@@ -53,7 +53,7 @@ ThrowExcelErrorValueExceptionIf( () => criteria == null || criteria.ToString().Length > 255, eErrorType.Value); - var retVal = 0d; + double retVal; if (arguments.Count() > 2) { var sumRange = arguments.ElementAt(2).Value as ExcelDataProvider.IRangeInfo; //IEnumerable<FunctionArgument>; retVal = CalculateWithSumRange(args, criteria.ToString(), sumRange, context);
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs b/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs index d12ddf8..6061b74 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Math/VarMethods.cs
@@ -39,13 +39,13 @@ public static double Var(IEnumerable<double> args) { double avg = args.Average(); - double d = args.Aggregate(0.0, (total, next) => total += System.Math.Pow(next - avg, 2)); + double d = args.Aggregate(0.0, (total, next) => total + System.Math.Pow(next - avg, 2)); return Divide(d, (args.Count() - 1)); } public static double VarP(IEnumerable<double> args) { double avg = args.Average(); - double d = args.Aggregate(0.0, (total, next) => total += System.Math.Pow(next - avg, 2)); + double d = args.Aggregate(0.0, (total, next) => total + System.Math.Pow(next - avg, 2)); return Divide(d, args.Count()); } }
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs index 2a05519..e5ee106 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
@@ -24,7 +24,7 @@ ParsingContext context) { ValidateArguments(arguments, 1); var val = ArgToString(arguments, 0).TrimEnd(' '); - double result = 0d; + double result; if (Regex.IsMatch( val, $"^[\\d]*({Regex.Escape(_groupSeparator)}?[\\d]*)?({Regex.Escape(_decimalSeparator)
diff --git a/EPPlus/FormulaParsing/ExcelCalculationOption.cs b/EPPlus/FormulaParsing/ExcelCalculationOption.cs index a94d5ef..79d03bc 100644 --- a/EPPlus/FormulaParsing/ExcelCalculationOption.cs +++ b/EPPlus/FormulaParsing/ExcelCalculationOption.cs
@@ -1,9 +1,5 @@ namespace OfficeOpenXml.FormulaParsing; public class ExcelCalculationOption { - public ExcelCalculationOption() { - AllowCirculareReferences = false; - } - - public bool AllowCirculareReferences { get; set; } + public bool AllowCirculareReferences { get; set; } = false; }
diff --git a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs index afba2c1..c7dd453 100644 --- a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs +++ b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressInfo.cs
@@ -36,7 +36,6 @@ public class ExcelAddressInfo { private ExcelAddressInfo(string address) { var addressOnSheet = address; - Worksheet = string.Empty; if (address.Contains("!")) { var worksheetArr = address.Split('!'); Worksheet = worksheetArr[0]; @@ -57,7 +56,7 @@ return new(address); } - public string Worksheet { get; private set; } + public string Worksheet { get; private set; } = string.Empty; public bool WorksheetIsSpecified => !string.IsNullOrEmpty(Worksheet);
diff --git a/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs b/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs index cf62744..b46145c 100644 --- a/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs +++ b/EPPlus/FormulaParsing/ExcelUtilities/RangeAddress.cs
@@ -32,11 +32,7 @@ namespace OfficeOpenXml.FormulaParsing.ExcelUtilities; public class RangeAddress { - public RangeAddress() { - Address = string.Empty; - } - - internal string Address { get; set; } + internal string Address { get; set; } = string.Empty; public string Worksheet { get; internal set; }
diff --git a/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs b/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs index e32e3f0..bdab90a 100644 --- a/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs +++ b/EPPlus/FormulaParsing/ExpressionGraph/Expression.cs
@@ -77,7 +77,7 @@ } public virtual Expression MergeWithNext() { - var expression = this; + Expression expression; if (Next != null && Operator != null) { var result = Operator.Apply(Compile(), Next.Compile()); expression = ExpressionConverter.Instance.FromCompileResult(result);
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs index 6ba0d9b..4639753 100644 --- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs +++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenFactory.cs
@@ -70,9 +70,9 @@ var tokenList = (IList<Token>)tokens; //Address with worksheet-string before /JK if (token.StartsWith("!") && tokenList[tokenList.Count - 1].TokenType == TokenType.String) { - string addr = ""; var i = tokenList.Count - 2; if (i > 0) { + string addr; if (tokenList[i].TokenType == TokenType.StringContent) { addr = "'" + tokenList[i].Value.Replace("'", "''") + "'"; } else {
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs index 337f2a2..d56dc18 100644 --- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs +++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorProvider.cs
@@ -34,10 +34,9 @@ namespace OfficeOpenXml.FormulaParsing.LexicalAnalysis; public class TokenSeparatorProvider : ITokenSeparatorProvider { - private static readonly Dictionary<string, Token> _tokens; + private static readonly Dictionary<string, Token> _tokens = new(); static TokenSeparatorProvider() { - _tokens = new(); _tokens.Add("+", new("+", TokenType.Operator)); _tokens.Add("-", new("-", TokenType.Operator)); _tokens.Add("*", new("*", TokenType.Operator));
diff --git a/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs b/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs index ef9c31a..130324d 100644 --- a/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs +++ b/EPPlus/FormulaParsing/LexicalAnalysis/TokenizerContext.cs
@@ -40,13 +40,11 @@ if (!string.IsNullOrEmpty(formula)) { _chars = formula.ToArray(); } - _result = new(); - _currentToken = new(); } private readonly char[] _chars; - private readonly List<Token> _result; - private StringBuilder _currentToken; + private readonly List<Token> _result = new(); + private StringBuilder _currentToken = new(); public char[] FormulaChars => _chars;
diff --git a/EPPlus/FormulaParsing/ParsingConfiguration.cs b/EPPlus/FormulaParsing/ParsingConfiguration.cs index 71440b3..90792b4 100644 --- a/EPPlus/FormulaParsing/ParsingConfiguration.cs +++ b/EPPlus/FormulaParsing/ParsingConfiguration.cs
@@ -15,11 +15,9 @@ public IExpressionCompiler ExpressionCompiler { get; private set; } - public FunctionRepository FunctionRepository { get; private set; } + public FunctionRepository FunctionRepository { get; private set; } = FunctionRepository.Create(); - private ParsingConfiguration() { - FunctionRepository = FunctionRepository.Create(); - } + private ParsingConfiguration() {} internal static ParsingConfiguration Create() { return new();
diff --git a/EPPlus/FormulaParsing/ParsingScope.cs b/EPPlus/FormulaParsing/ParsingScope.cs index 1e861c5..f901f1e 100644 --- a/EPPlus/FormulaParsing/ParsingScope.cs +++ b/EPPlus/FormulaParsing/ParsingScope.cs
@@ -16,13 +16,12 @@ _parsingScopes = parsingScopes; Parent = parent; Address = address; - ScopeId = Guid.NewGuid(); } /// <summary> /// Id of the scope. /// </summary> - public Guid ScopeId { get; private set; } + public Guid ScopeId { get; private set; } = Guid.NewGuid(); /// <summary> /// The calling scope.
diff --git a/EPPlus/OfficeProperties.cs b/EPPlus/OfficeProperties.cs index 1b0a6be..e41d226 100644 --- a/EPPlus/OfficeProperties.cs +++ b/EPPlus/OfficeProperties.cs
@@ -34,10 +34,7 @@ using System; using System.Globalization; -using System.IO; using System.Xml; -using OfficeOpenXml.Packaging; -using OfficeOpenXml.Utils; namespace OfficeOpenXml; @@ -45,18 +42,14 @@ /// Provides access to the properties bag of the package /// </summary> public sealed class OfficeProperties : XmlHelper { - private XmlDocument _xmlPropertiesCore; - private XmlDocument _xmlPropertiesExtended; - private XmlDocument _xmlPropertiesCustom; + private readonly XmlDocument _xmlPropertiesCore; + private readonly XmlDocument _xmlPropertiesExtended; private readonly Uri _uriPropertiesCore = new("/docProps/core.xml", UriKind.Relative); private readonly Uri _uriPropertiesExtended = new("/docProps/app.xml", UriKind.Relative); - private readonly Uri _uriPropertiesCustom = new("/docProps/custom.xml", UriKind.Relative); private readonly XmlHelper _coreHelper; private readonly XmlHelper _extendedHelper; - private XmlHelper _customHelper; - private readonly ExcelPackage _package; /// <summary> /// Provides access to all the office document properties. @@ -65,67 +58,25 @@ /// <param name="ns"></param> internal OfficeProperties(ExcelPackage package, XmlNamespaceManager ns) : base(ns) { - _package = package; - + const string coreBaseXml = + $"""<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><cp:coreProperties xmlns:cp="{ExcelPackage._schemaCore}" xmlns:dc="{ExcelPackage._schemaDc}" xmlns:dcterms="{ExcelPackage._schemaDcTerms}" xmlns:dcmitype="{ExcelPackage._schemaDcmiType}" xmlns:xsi="{ExcelPackage._schemaXsi}"></cp:coreProperties>"""; + _xmlPropertiesCore = package.GetOrCreateXmlDocument( + _uriPropertiesCore, + "application/vnd.openxmlformats-package.core-properties+xml", + "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", + coreBaseXml); _coreHelper = XmlHelperFactory.Create( ns, - CorePropertiesXml.SelectSingleNode("cp:coreProperties", NameSpaceManager)); - _extendedHelper = XmlHelperFactory.Create(ns, ExtendedPropertiesXml); - _customHelper = XmlHelperFactory.Create(ns, CustomPropertiesXml); - } + _xmlPropertiesCore.SelectSingleNode("cp:coreProperties", NameSpaceManager)); - /// <summary> - /// Provides access to the XML document that holds all the code - /// document properties. - /// </summary> - public XmlDocument CorePropertiesXml { - get { - if (_xmlPropertiesCore == null) { - string xml = string.Format( - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><cp:coreProperties xmlns:cp=\"{0}\" xmlns:dc=\"{1}\" xmlns:dcterms=\"{2}\" xmlns:dcmitype=\"{3}\" xmlns:xsi=\"{4}\"></cp:coreProperties>", - ExcelPackage._schemaCore, - ExcelPackage._schemaDc, - ExcelPackage._schemaDcTerms, - ExcelPackage._schemaDcmiType, - ExcelPackage._schemaXsi); - - _xmlPropertiesCore = GetXmlDocument( - xml, - _uriPropertiesCore, - "application/vnd.openxmlformats-package.core-properties+xml", - "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"); - } - return (_xmlPropertiesCore); - } - } - - private XmlDocument GetXmlDocument( - string startXml, - Uri uri, - string contentType, - string relationship) { - XmlDocument xmlDoc; - if (_package.Package.PartExists(uri)) { - xmlDoc = _package.GetXmlFromUri(uri); - } else { - xmlDoc = new(); - xmlDoc.LoadXml(startXml); - - // Create a the part and add to the package - ZipPackagePart part = _package.Package.CreatePart(uri, contentType); - - // Save it to the package - StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write)); - xmlDoc.Save(stream); - //stream.Close(); - - // create the relationship between the workbook and the new shared strings part - _package.Package.CreateRelationship( - UriHelper.GetRelativeUri(new("/xl", UriKind.Relative), uri), - TargetMode.Internal, - relationship); - } - return xmlDoc; + const string extendedBaseXml = + $"""<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><Properties xmlns:vt="{ExcelPackage._schemaVt}" xmlns="{ExcelPackage._schemaExtended}"></Properties>"""; + _xmlPropertiesExtended = package.GetOrCreateXmlDocument( + _uriPropertiesExtended, + "application/vnd.openxmlformats-officedocument.extended-properties+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", + extendedBaseXml); + _extendedHelper = XmlHelperFactory.Create(ns, _xmlPropertiesExtended); } private const string _titlePath = "dc:title"; @@ -240,16 +191,7 @@ /// </summary> public XmlDocument ExtendedPropertiesXml { get { - if (_xmlPropertiesExtended == null) { - _xmlPropertiesExtended = GetXmlDocument( - string.Format( - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>", - ExcelPackage._schemaVt, - ExcelPackage._schemaExtended), - _uriPropertiesExtended, - "application/vnd.openxmlformats-officedocument.extended-properties+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"); - } + if (_xmlPropertiesExtended == null) {} return (_xmlPropertiesExtended); } } @@ -320,142 +262,4 @@ _coreHelper.SetXmlNodeString(_modifiedPath + "/@xsi:type", "dcterms:W3CDTF"); } } - - /// <summary> - /// Provides access to the XML document which holds the document's custom properties - /// </summary> - public XmlDocument CustomPropertiesXml { - get { - if (_xmlPropertiesCustom == null) { - _xmlPropertiesCustom = GetXmlDocument( - string.Format( - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><Properties xmlns:vt=\"{0}\" xmlns=\"{1}\"></Properties>", - ExcelPackage._schemaVt, - ExcelPackage._schemaCustom), - _uriPropertiesCustom, - "application/vnd.openxmlformats-officedocument.custom-properties+xml", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"); - } - return (_xmlPropertiesCustom); - } - } - - /// <summary> - /// Gets the value of a custom property - /// </summary> - /// <param name="propertyName">The name of the property</param> - /// <returns>The current value of the property</returns> - public object GetCustomPropertyValue(string propertyName) { - string searchString = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName); - XmlElement node = - CustomPropertiesXml.SelectSingleNode(searchString, NameSpaceManager) as XmlElement; - if (node != null) { - string value = node.LastChild.InnerText; - switch (node.LastChild.LocalName) { - case "filetime": - if (DateTime.TryParse(value, out var dt)) { - return dt; - } - return null; - case "i4": - if (int.TryParse(value, out var i)) { - return i; - } - return null; - case "r8": - if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { - return d; - } - return null; - case "bool": - if (value == "true") { - return true; - } - if (value == "false") { - return false; - } - return null; - default: - return value; - } - } - return null; - } - - /// <summary> - /// Allows you to set the value of a current custom property or create your own custom property. - /// </summary> - /// <param name="propertyName">The name of the property</param> - /// <param name="value">The value of the property</param> - public void SetCustomPropertyValue(string propertyName, object value) { - XmlNode allProps = CustomPropertiesXml.SelectSingleNode("ctp:Properties", NameSpaceManager); - - var prop = string.Format("ctp:Properties/ctp:property[@name='{0}']", propertyName); - XmlElement node = CustomPropertiesXml.SelectSingleNode(prop, NameSpaceManager) as XmlElement; - if (node == null) { - int pid; - var maxNode = CustomPropertiesXml.SelectSingleNode( - "ctp:Properties/ctp:property[not(@pid <= preceding-sibling::ctp:property/@pid) and not(@pid <= following-sibling::ctp:property/@pid)]", - NameSpaceManager); - if (maxNode == null) { - pid = 2; - } else { - if (!int.TryParse(maxNode.Attributes["pid"].Value, out pid)) { - pid = 2; - } - pid++; - } - node = CustomPropertiesXml.CreateElement("property", ExcelPackage._schemaCustom); - node.SetAttribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); - node.SetAttribute("pid", pid.ToString()); // custom property pid - node.SetAttribute("name", propertyName); - - allProps.AppendChild(node); - } else { - while (node.ChildNodes.Count > 0) { - node.RemoveChild(node.ChildNodes[0]); - } - } - XmlElement valueElem; - if (value is bool) { - valueElem = CustomPropertiesXml.CreateElement("vt", "bool", ExcelPackage._schemaVt); - valueElem.InnerText = value.ToString().ToLower(CultureInfo.InvariantCulture); - } else if (value is DateTime time) { - valueElem = CustomPropertiesXml.CreateElement("vt", "filetime", ExcelPackage._schemaVt); - valueElem.InnerText = time.AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ"); - } else if (value is short || value is int) { - valueElem = CustomPropertiesXml.CreateElement("vt", "i4", ExcelPackage._schemaVt); - valueElem.InnerText = value.ToString(); - } else if (value is double || value is decimal || value is float || value is long) { - valueElem = CustomPropertiesXml.CreateElement("vt", "r8", ExcelPackage._schemaVt); - if (value is double d) { - valueElem.InnerText = d.ToString(CultureInfo.InvariantCulture); - } else if (value is float f) { - valueElem.InnerText = f.ToString(CultureInfo.InvariantCulture); - } else if (value is decimal value1) { - valueElem.InnerText = value1.ToString(CultureInfo.InvariantCulture); - } else { - valueElem.InnerText = value.ToString(); - } - } else { - valueElem = CustomPropertiesXml.CreateElement("vt", "lpwstr", ExcelPackage._schemaVt); - valueElem.InnerText = value.ToString(); - } - node.AppendChild(valueElem); - } - - /// <summary> - /// Saves the document properties back to the package. - /// </summary> - internal void Save() { - if (_xmlPropertiesCore != null) { - _package.SavePart(_uriPropertiesCore, _xmlPropertiesCore); - } - if (_xmlPropertiesExtended != null) { - _package.SavePart(_uriPropertiesExtended, _xmlPropertiesExtended); - } - if (_xmlPropertiesCustom != null) { - _package.SavePart(_uriPropertiesCustom, _xmlPropertiesCustom); - } - } }
diff --git a/EPPlus/Packaging/ZipPackage.cs b/EPPlus/Packaging/ZipPackage.cs index 843d105..921c744 100644 --- a/EPPlus/Packaging/ZipPackage.cs +++ b/EPPlus/Packaging/ZipPackage.cs
@@ -35,6 +35,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using OfficeOpenXml.Utils; @@ -59,7 +60,7 @@ /// <summary> /// Represent an OOXML Zip package. /// </summary> -public class ZipPackage : ZipPackageRelationshipBase { +internal class ZipPackage : ZipPackageRelationshipBase { internal class ContentType { internal string Name; internal bool IsExtension; @@ -111,10 +112,13 @@ if (e.FullName.EndsWith(".rels", StringComparison.InvariantCultureIgnoreCase)) { rels.Add(GetUriKey(e.FullName), e); } else { + var data = new byte[e.Length]; using var inputStream = e.Open(); - var part = new ZipPackagePart(this, e); - part.Stream = new(); - inputStream.CopyTo(part.Stream); + inputStream.ReadExactly(data); + var part = new ZipPackagePart( + this, + e, + ImmutableCollectionsMarshal.AsImmutableArray(data)); Parts.Add(GetUriKey(e.FullName), part); } } @@ -158,24 +162,16 @@ } } - internal ZipPackagePart CreatePart(Uri partUri, string contentType) { - return CreatePart(partUri, contentType, CompressionLevel.Default); - } - - internal ZipPackagePart CreatePart( - Uri partUri, - string contentType, - CompressionLevel compressionLevel) { + internal void CreatePart(Uri partUri, string contentType, Action<StreamWriter> saveHandler) { if (PartExists(partUri)) { throw (new InvalidOperationException("Part already exist")); } - var part = new ZipPackagePart(this, partUri, contentType, compressionLevel); + var part = new ZipPackagePart(this, partUri, contentType, saveHandler); _contentTypes.Add( GetUriKey(part.Uri.OriginalString), new(contentType, false, part.Uri.OriginalString)); Parts.Add(GetUriKey(part.Uri.OriginalString), part); - return part; } internal ZipPackagePart GetPart(Uri partUri) { @@ -223,7 +219,6 @@ while (rels.Count > 0) { rels.Remove(rels.First().Id); } - rels = null; _contentTypes.Remove(GetUriKey(uri.OriginalString)); //remove all relations Parts.Remove(GetUriKey(uri.OriginalString)); @@ -231,13 +226,13 @@ internal void Save(Stream stream) { using var zipArchive = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true); - /**** ContentType****/ + // Content types var contentTypesEntry = zipArchive.CreateEntry("[Content_Types].xml"); using (var contentTypesWriter = new StreamWriter(contentTypesEntry.Open())) { contentTypesWriter.Write(GetContentTypeXml()); } - /**** Top Rels ****/ - _rels.WriteZip(zipArchive, "_rels\\.rels"); + // Top Rels + _rels.WriteZip(zipArchive, "_rels/.rels"); ZipPackagePart ssPart = null; foreach (var part in Parts.Values) { if (part.ContentType != ExcelPackage._contentTypeSharedString) { @@ -250,7 +245,6 @@ if (ssPart != null) { ssPart.WriteZip(zipArchive); } - //return ms; } private string GetContentTypeXml() { @@ -258,29 +252,12 @@ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); foreach (ContentType ct in _contentTypes.Values) { if (ct.IsExtension) { - xml.AppendFormat("<Default ContentType=\"{0}\" Extension=\"{1}\"/>", ct.Name, ct.Match); + xml.Append($"<Default ContentType=\"{ct.Name}\" Extension=\"{ct.Match}\"/>"); } else { - xml.AppendFormat( - "<Override ContentType=\"{0}\" PartName=\"{1}\" />", - ct.Name, - GetUriKey(ct.Match)); + xml.Append($"<Override ContentType=\"{ct.Name}\" PartName=\"{GetUriKey(ct.Match)}\" />"); } } xml.Append("</Types>"); return xml.ToString(); } - - private CompressionLevel _compression = CompressionLevel.Default; - - public CompressionLevel Compression { - get => _compression; - set { - foreach (var part in Parts.Values) { - if (part.CompressionLevel == _compression) { - part.CompressionLevel = value; - } - } - _compression = value; - } - } }
diff --git a/EPPlus/Packaging/ZipPackagePart.cs b/EPPlus/Packaging/ZipPackagePart.cs index 407428d..47d20fb 100644 --- a/EPPlus/Packaging/ZipPackagePart.cs +++ b/EPPlus/Packaging/ZipPackagePart.cs
@@ -30,42 +30,35 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.IO; using System.IO.Compression; +using System.Runtime.InteropServices; namespace OfficeOpenXml.Packaging; internal class ZipPackagePart : ZipPackageRelationshipBase, IDisposable { - internal delegate void SaveHandlerDelegate(StreamWriter streamWriter); - - internal ZipPackagePart(ZipPackage package, ZipArchiveEntry entry) { + internal ZipPackagePart(ZipPackage package, ZipArchiveEntry entry, ImmutableArray<byte> data) { Package = package; SaveHandler = null; Uri = new(package.GetUriKey(entry.FullName), UriKind.Relative); + _data = data; } + private readonly ImmutableArray<byte> _data; + internal ZipPackagePart( ZipPackage package, Uri partUri, string contentType, - CompressionLevel compressionLevel) { + Action<StreamWriter> saveHandler) { Package = package; - //Entry = new ZipEntry(); - //Entry.FileName = partUri.OriginalString.Replace('/','\\'); Uri = partUri; ContentType = contentType; - CompressionLevel = compressionLevel; + SaveHandler = saveHandler; } - internal ZipPackage Package { get; set; } - - internal CompressionLevel CompressionLevel; - private MemoryStream _stream; - - internal MemoryStream Stream { - get => _stream; - set => _stream = value; - } + private ZipPackage Package { get; } internal override ZipPackageRelationship CreateRelationship( Uri targetUri, @@ -77,20 +70,7 @@ } internal MemoryStream GetStream() { - return GetStream(FileMode.OpenOrCreate, FileAccess.ReadWrite); - } - - internal MemoryStream GetStream(FileMode fileMode) { - return GetStream(FileMode.Create, FileAccess.ReadWrite); - } - - internal MemoryStream GetStream(FileMode fileMode, FileAccess fileAccess) { - if (_stream == null || fileMode == FileMode.CreateNew || fileMode == FileMode.Create) { - _stream = new(); - } else { - _stream.Seek(0, SeekOrigin.Begin); - } - return _stream; + return new(ImmutableCollectionsMarshal.AsArray(_data) ?? [], false); } private string _contentType = ""; @@ -110,22 +90,18 @@ } } - public Uri Uri { get; private set; } + public Uri Uri { get; } - internal SaveHandlerDelegate SaveHandler { get; set; } + internal Action<StreamWriter> SaveHandler { get; set; } internal void WriteZip(ZipArchive zipArchive) { - byte[] b; if (SaveHandler == null) { - b = GetStream().ToArray(); - if (b.Length - == 0) //Make sure the file isn't empty. DotNetZip streams does not seems to handle zero sized files. - { + if (_data.Length == 0) { return; } var zipEntry = zipArchive.CreateEntry(Uri.OriginalString); using var os = zipEntry.Open(); - os.Write(b); + os.Write(_data.AsSpan()); } else { var zipEntry = zipArchive.CreateEntry(Uri.OriginalString); using var streamWriter = new StreamWriter(zipEntry.Open()); @@ -137,11 +113,7 @@ var name = Path.GetFileName(f); _rels.WriteZip(zipArchive, $"{f.Substring(0, f.Length - name.Length)}_rels/{name}.rels"); } - b = null; } - public void Dispose() { - _stream.Close(); - _stream.Dispose(); - } + public void Dispose() {} }
diff --git a/EPPlus/Packaging/ZipPackageRelationship.cs b/EPPlus/Packaging/ZipPackageRelationship.cs index 15c4539..1d76501 100644 --- a/EPPlus/Packaging/ZipPackageRelationship.cs +++ b/EPPlus/Packaging/ZipPackageRelationship.cs
@@ -33,7 +33,7 @@ namespace OfficeOpenXml.Packaging; -public class ZipPackageRelationship { +internal class ZipPackageRelationship { public Uri TargetUri { get; internal set; } public Uri SourceUri { get; internal set; }
diff --git a/EPPlus/Packaging/ZipPackageRelationshipBase.cs b/EPPlus/Packaging/ZipPackageRelationshipBase.cs index 882358e..ff9bf8f 100644 --- a/EPPlus/Packaging/ZipPackageRelationshipBase.cs +++ b/EPPlus/Packaging/ZipPackageRelationshipBase.cs
@@ -35,7 +35,7 @@ namespace OfficeOpenXml.Packaging; -public abstract class ZipPackageRelationshipBase { +internal abstract class ZipPackageRelationshipBase { protected ZipPackageRelationshipCollection _rels = new(); protected internal int maxRId = 1;
diff --git a/EPPlus/Packaging/ZipPackageRelationshipCollection.cs b/EPPlus/Packaging/ZipPackageRelationshipCollection.cs index f828f61..7890ad6 100644 --- a/EPPlus/Packaging/ZipPackageRelationshipCollection.cs +++ b/EPPlus/Packaging/ZipPackageRelationshipCollection.cs
@@ -38,7 +38,7 @@ namespace OfficeOpenXml.Packaging; -public class ZipPackageRelationshipCollection : IEnumerable<ZipPackageRelationship> { +internal class ZipPackageRelationshipCollection : IEnumerable<ZipPackageRelationship> { protected internal Dictionary<string, ZipPackageRelationship> _rels = new( StringComparer.InvariantCultureIgnoreCase);
diff --git a/EPPlus/Style/Dxf/DxfStyleBase.cs b/EPPlus/Style/Dxf/DxfStyleBase.cs index 0c73cb0..b81e07a 100644 --- a/EPPlus/Style/Dxf/DxfStyleBase.cs +++ b/EPPlus/Style/Dxf/DxfStyleBase.cs
@@ -8,7 +8,6 @@ internal DxfStyleBase(ExcelStyles styles) { _styles = styles; - AllowChange = false; //Don't touch this value in the styles.xml (by default). When Dxfs is fully implemented this can be removed. } protected internal abstract string Id { get; } @@ -75,5 +74,5 @@ /// <summary> /// Is this value allowed to be changed? /// </summary> - protected internal bool AllowChange { get; set; } + protected internal bool AllowChange { get; set; } = false; }
diff --git a/EPPlus/Style/Dxf/ExcelDxfStyle.cs b/EPPlus/Style/Dxf/ExcelDxfStyle.cs index 325d5b1..4784a1e 100644 --- a/EPPlus/Style/Dxf/ExcelDxfStyle.cs +++ b/EPPlus/Style/Dxf/ExcelDxfStyle.cs
@@ -1,11 +1,22 @@ using System; +using System.Collections.Immutable; using System.Globalization; using System.Xml; namespace OfficeOpenXml.Style.Dxf; public class ExcelDxfStyleConditionalFormatting : DxfStyleBase<ExcelDxfStyleConditionalFormatting> { - private readonly XmlHelperInstance _helper; + private readonly DxfStyleXmlHelper _helper; + + private class DxfStyleXmlHelper(XmlNamespaceManager nameSpaceManager, XmlNode topNode) + : XmlHelper(nameSpaceManager, topNode) { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "font", + "numFmt", + "fill", + "border", + ]; + } internal ExcelDxfStyleConditionalFormatting( XmlNamespaceManager nameSpaceManager, @@ -40,12 +51,11 @@ Fill.BackgroundColor = GetColor(_helper, "d:fill/d:patternFill/d:bgColor/"); Fill.PatternColor = GetColor(_helper, "d:fill/d:patternFill/d:fgColor/"); } else { - _helper = new(nameSpaceManager); + _helper = new(nameSpaceManager, null); } - _helper.SchemaNodeOrder = new[] { "font", "numFmt", "fill", "border" }; } - private ExcelDxfBorderItem GetBorderItem(XmlHelperInstance helper, string path) { + private ExcelDxfBorderItem GetBorderItem(XmlHelper helper, string path) { ExcelDxfBorderItem bi = new ExcelDxfBorderItem(_styles); bi.Style = GetBorderStyleEnum(helper.GetXmlNodeString(path + "/@style")); bi.Color = GetColor(helper, path + "/d:color"); @@ -58,7 +68,7 @@ private ExcelFillStyle GetPatternTypeEnum(string patternType) => Enum.TryParse<ExcelFillStyle>(patternType, true, out var result) ? result : ExcelFillStyle.None; - private ExcelDxfColor GetColor(XmlHelperInstance helper, string path) { + private ExcelDxfColor GetColor(XmlHelper helper, string path) { ExcelDxfColor ret = new ExcelDxfColor(_styles); ret.Theme = helper.GetXmlNodeIntNull(path + "/@theme"); ret.Index = helper.GetXmlNodeIntNull(path + "/@indexed");
diff --git a/EPPlus/Style/ExcelBorderItem.cs b/EPPlus/Style/ExcelBorderItem.cs index 6f8208b..89dc283 100644 --- a/EPPlus/Style/ExcelBorderItem.cs +++ b/EPPlus/Style/ExcelBorderItem.cs
@@ -30,7 +30,6 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ -using System; using OfficeOpenXml.Style.XmlAccess; namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelColor.cs b/EPPlus/Style/ExcelColor.cs index 8725905..e02de45 100644 --- a/EPPlus/Style/ExcelColor.cs +++ b/EPPlus/Style/ExcelColor.cs
@@ -30,7 +30,6 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ -using System; using OfficeOpenXml.Style.XmlAccess; namespace OfficeOpenXml.Style; @@ -103,98 +102,4 @@ internal override void SetIndex(int index) { _parent.Index = index; } - - /// <summary> - /// Return the RGB value for the color object that uses the Indexed or Tint property - /// </summary> - /// <param name="theColor">The color object</param> - /// <returns>The RGB color starting with a #</returns> - public string LookupColor(ExcelColor theColor) { - //Thanks to neaves for contributing this method. - int iTint = 0; - string translatedRgb = ""; - - // reference extracted from ECMA-376, Part 4, Section 3.8.26 - string[] rgbLookup = { - "#FF000000", // 0 - "#FFFFFFFF", - "#FFFF0000", - "#FF00FF00", - "#FF0000FF", - "#FFFFFF00", - "#FFFF00FF", - "#FF00FFFF", - "#FF000000", // 8 - "#FFFFFFFF", - "#FFFF0000", - "#FF00FF00", - "#FF0000FF", - "#FFFFFF00", - "#FFFF00FF", - "#FF00FFFF", - "#FF800000", - "#FF008000", - "#FF000080", - "#FF808000", - "#FF800080", - "#FF008080", - "#FFC0C0C0", - "#FF808080", - "#FF9999FF", - "#FF993366", - "#FFFFFFCC", - "#FFCCFFFF", - "#FF660066", - "#FFFF8080", - "#FF0066CC", - "#FFCCCCFF", - "#FF000080", - "#FFFF00FF", - "#FFFFFF00", - "#FF00FFFF", - "#FF800080", - "#FF800000", - "#FF008080", - "#FF0000FF", - "#FF00CCFF", - "#FFCCFFFF", - "#FFCCFFCC", - "#FFFFFF99", - "#FF99CCFF", - "#FFFF99CC", - "#FFCC99FF", - "#FFFFCC99", - "#FF3366FF", - "#FF33CCCC", - "#FF99CC00", - "#FFFFCC00", - "#FFFF9900", - "#FFFF6600", - "#FF666699", - "#FF969696", - "#FF003366", - "#FF339966", - "#FF003300", - "#FF333300", - "#FF993300", - "#FF993366", - "#FF333399", - "#FF333333", // 63 - }; - - if ((0 <= theColor.Indexed) && (rgbLookup.Length > theColor.Indexed)) { - // coloring by pre-set color codes - translatedRgb = rgbLookup[theColor.Indexed]; - } else if (null != theColor.Rgb && 0 < theColor.Rgb.Length) { - // coloring by RGB value ("FFRRGGBB") - translatedRgb = "#" + theColor.Rgb; - } else { - // coloring by shades of grey (-1 -> 0) - iTint = ((int)(theColor.Tint * 160) + 0x80); - translatedRgb = ((int)(decimal.Round(theColor.Tint * -512))).ToString("X"); - translatedRgb = "#FF" + translatedRgb + translatedRgb + translatedRgb; - } - - return translatedRgb; - } }
diff --git a/EPPlus/Style/ExcelGradientFill.cs b/EPPlus/Style/ExcelGradientFill.cs index 7103b37..4ab355e 100644 --- a/EPPlus/Style/ExcelGradientFill.cs +++ b/EPPlus/Style/ExcelGradientFill.cs
@@ -30,7 +30,6 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ -using System; using OfficeOpenXml.Style.XmlAccess; namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelParagraph.cs b/EPPlus/Style/ExcelParagraph.cs deleted file mode 100644 index a758a83..0000000 --- a/EPPlus/Style/ExcelParagraph.cs +++ /dev/null
@@ -1,60 +0,0 @@ -/******************************************************************************* - * You may amend and distribute as you like, but don't remove this header! - * - * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. - * See http://www.codeplex.com/EPPlus for details. - * - * Copyright (C) 2011 Jan Källman - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php - * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html - * - * All code and executables are provided "as is" with no warranty either express or implied. - * The author accepts no liability for any damage or loss of business that this product may cause. - * - * Code change notes: - * - * Author Change Date - * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 - *******************************************************************************/ - -using System.Xml; - -namespace OfficeOpenXml.Style; - -/// <summary> -/// Handels paragraph text -/// </summary> -public sealed class ExcelParagraph : ExcelTextFont { - public ExcelParagraph( - XmlNamespaceManager ns, - XmlNode rootNode, - string path, - string[] schemaNodeOrder) - : base(ns, rootNode, path + "a:rPr", schemaNodeOrder) {} - - private const string _textPath = "../a:t"; - - /// <summary> - /// Text - /// </summary> - public string Text { - get => GetXmlNodeString(_textPath); - set { - CreateTopNode(); - SetXmlNodeString(_textPath, value); - } - } -}
diff --git a/EPPlus/Style/ExcelParagraphCollection.cs b/EPPlus/Style/ExcelParagraphCollection.cs deleted file mode 100644 index a74fe57..0000000 --- a/EPPlus/Style/ExcelParagraphCollection.cs +++ /dev/null
@@ -1,145 +0,0 @@ -/******************************************************************************* - * You may amend and distribute as you like, but don't remove this header! - * - * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. - * See http://www.codeplex.com/EPPlus for details. - * - * Copyright (C) 2011 Jan Källman - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php - * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html - * - * All code and executables are provided "as is" with no warranty either express or implied. - * The author accepts no liability for any damage or loss of business that this product may cause. - * - * Code change notes: - * - * Author Change Date - * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 - *******************************************************************************/ - -using System.Collections; -using System.Collections.Generic; -using System.Text; -using System.Xml; - -namespace OfficeOpenXml.Style; - -/// <summary> -/// A collection of Paragraph objects -/// </summary> -public class ExcelParagraphCollection : XmlHelper, IEnumerable<ExcelParagraph> { - private readonly List<ExcelParagraph> _list = new(); - private readonly string _path; - - internal ExcelParagraphCollection( - XmlNamespaceManager ns, - XmlNode topNode, - string path, - string[] schemaNodeOrder) - : base(ns, topNode) { - var nl = topNode.SelectNodes(path + "/a:r", NameSpaceManager); - SchemaNodeOrder = schemaNodeOrder; - if (nl != null) { - foreach (XmlNode n in nl) { - _list.Add(new(ns, n, "", schemaNodeOrder)); - } - } - _path = path; - } - - public ExcelParagraph this[int index] => _list[index]; - - public int Count => _list.Count; - - /// <summary> - /// Add a rich text string - /// </summary> - /// <param name="text">The text to add</param> - /// <returns></returns> - public ExcelParagraph Add(string text) { - XmlDocument doc; - if (TopNode is XmlDocument document) { - doc = document; - } else { - doc = TopNode.OwnerDocument; - } - XmlNode parentNode = TopNode.SelectSingleNode(_path, NameSpaceManager); - if (parentNode == null) { - CreateNode(_path); - parentNode = TopNode.SelectSingleNode(_path, NameSpaceManager); - } - - var node = doc.CreateElement("a", "r", ExcelPackage._schemaDrawings); - parentNode.AppendChild(node); - var childNode = doc.CreateElement("a", "rPr", ExcelPackage._schemaDrawings); - node.AppendChild(childNode); - var rt = new ExcelParagraph(NameSpaceManager, node, "", SchemaNodeOrder); - rt.ComplexFont = "Calibri"; - rt.LatinFont = "Calibri"; - rt.Size = 11; - - rt.Text = text; - _list.Add(rt); - return rt; - } - - public void Clear() { - _list.Clear(); - TopNode.RemoveAll(); - } - - public void RemoveAt(int index) { - var node = _list[index].TopNode; - while (node != null && node.Name != "a:r") { - node = node.ParentNode; - } - node.ParentNode.RemoveChild(node); - _list.RemoveAt(index); - } - - public void Remove(ExcelRichText item) { - TopNode.RemoveChild(item.TopNode); - } - - public string Text { - get { - StringBuilder sb = new StringBuilder(); - foreach (var item in _list) { - sb.Append(item.Text); - } - return sb.ToString(); - } - set { - if (Count == 0) { - Add(value); - } else { - this[0].Text = value; - int count = Count; - for (int ix = Count - 1; ix > 0; ix--) { - RemoveAt(ix); - } - } - } - } - - IEnumerator<ExcelParagraph> IEnumerable<ExcelParagraph>.GetEnumerator() { - return _list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return _list.GetEnumerator(); - } -}
diff --git a/EPPlus/Style/ExcelRichText.cs b/EPPlus/Style/ExcelRichText.cs index 2458430..3e9994e 100644 --- a/EPPlus/Style/ExcelRichText.cs +++ b/EPPlus/Style/ExcelRichText.cs
@@ -33,6 +33,7 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Globalization; using System.Xml; @@ -42,26 +43,27 @@ /// A richtext part /// </summary> public class ExcelRichText : XmlHelper { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "rPr", + "t", + "b", + "i", + "strike", + "u", + "vertAlign", + "sz", + "color", + "rFont", + "family", + "scheme", + "charset", + ]; + internal ExcelRichText( XmlNamespaceManager ns, XmlNode topNode, ExcelRichTextCollection collection) : base(ns, topNode) { - SchemaNodeOrder = new[] { - "rPr", - "t", - "b", - "i", - "strike", - "u", - "vertAlign", - "sz", - "color", - "rFont", - "family", - "scheme", - "charset", - }; _collection = collection; }
diff --git a/EPPlus/Style/ExcelRichTextCollection.cs b/EPPlus/Style/ExcelRichTextCollection.cs index 62925f7..21f4803 100644 --- a/EPPlus/Style/ExcelRichTextCollection.cs +++ b/EPPlus/Style/ExcelRichTextCollection.cs
@@ -32,7 +32,6 @@ using System.Collections; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using System.Xml;
diff --git a/EPPlus/Style/ExcelStyle.cs b/EPPlus/Style/ExcelStyle.cs index 3292c7b..088b8c3 100644 --- a/EPPlus/Style/ExcelStyle.cs +++ b/EPPlus/Style/ExcelStyle.cs
@@ -30,7 +30,6 @@ * Jan Källman License changed GPL-->LGPL 2011-12-16 *******************************************************************************/ -using System; using OfficeOpenXml.Style.XmlAccess; namespace OfficeOpenXml.Style;
diff --git a/EPPlus/Style/ExcelTextFont.cs b/EPPlus/Style/ExcelTextFont.cs deleted file mode 100644 index 9252413..0000000 --- a/EPPlus/Style/ExcelTextFont.cs +++ /dev/null
@@ -1,222 +0,0 @@ -/******************************************************************************* - * You may amend and distribute as you like, but don't remove this header! - * - * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. - * See http://www.codeplex.com/EPPlus for details. - * - * Copyright (C) 2011 Jan Källman - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php - * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html - * - * All code and executables are provided "as is" with no warranty either express or implied. - * The author accepts no liability for any damage or loss of business that this product may cause. - * - * Code change notes: - * - * Author Change Date - * ****************************************************************************** - * Jan Källman Initial Release 2009-10-01 - * Jan Källman License changed GPL-->LGPL 2011-12-16 - *******************************************************************************/ - -using System; -using System.Globalization; -using System.Xml; - -namespace OfficeOpenXml.Style; - -/// <summary> -/// Linestyle -/// </summary> -public enum eUnderLineType { - Dash, - DashHeavy, - DashLong, - DashLongHeavy, - Double, - DotDash, - DotDashHeavy, - DotDotDash, - DotDotDashHeavy, - Dotted, - DottedHeavy, - Heavy, - None, - Single, - Wavy, - WavyDbl, - WavyHeavy, - Words, -} - -/// <summary> -/// Type of font strike -/// </summary> -public enum eStrikeType { - Double, - No, - Single, -} - -/// <summary> -/// Used by Rich-text and Paragraphs. -/// </summary> -public class ExcelTextFont : XmlHelper { - private readonly string _path; - private readonly XmlNode _rootNode; - - internal ExcelTextFont( - XmlNamespaceManager namespaceManager, - XmlNode rootNode, - string path, - string[] schemaNodeOrder) - : base(namespaceManager, rootNode) { - SchemaNodeOrder = schemaNodeOrder; - _rootNode = rootNode; - if (path != "") { - XmlNode node = rootNode.SelectSingleNode(path, namespaceManager); - if (node != null) { - TopNode = node; - } - } - _path = path; - } - - private readonly string _fontLatinPath = "a:latin/@typeface"; - - public string LatinFont { - get => GetXmlNodeString(_fontLatinPath); - set { - CreateTopNode(); - SetXmlNodeString(_fontLatinPath, value); - } - } - - protected internal void CreateTopNode() { - if (_path != "" && TopNode == _rootNode) { - CreateNode(_path); - TopNode = _rootNode.SelectSingleNode(_path, NameSpaceManager); - } - } - - private readonly string _fontCsPath = "a:cs/@typeface"; - - public string ComplexFont { - get => GetXmlNodeString(_fontCsPath); - set { - CreateTopNode(); - SetXmlNodeString(_fontCsPath, value); - } - } - - private readonly string _boldPath = "@b"; - - public bool Bold { - get => GetXmlNodeBool(_boldPath); - set { - CreateTopNode(); - SetXmlNodeString(_boldPath, value ? "1" : "0"); - } - } - - private readonly string _underLinePath = "@u"; - - public eUnderLineType UnderLine { - get => TranslateUnderline(GetXmlNodeString(_underLinePath)); - set { - CreateTopNode(); - SetXmlNodeString(_underLinePath, TranslateUnderlineText(value)); - } - } - - private readonly string _italicPath = "@i"; - - public bool Italic { - get => GetXmlNodeBool(_italicPath); - set { - CreateTopNode(); - SetXmlNodeString(_italicPath, value ? "1" : "0"); - } - } - - private readonly string _strikePath = "@strike"; - - public eStrikeType Strike { - get => TranslateStrike(GetXmlNodeString(_strikePath)); - set { - CreateTopNode(); - SetXmlNodeString(_strikePath, TranslateStrikeText(value)); - } - } - - private readonly string _sizePath = "@sz"; - - public float Size { - get => GetXmlNodeInt(_sizePath) / 100; - set { - CreateTopNode(); - SetXmlNodeString(_sizePath, ((int)(value * 100)).ToString()); - } - } - - private eUnderLineType TranslateUnderline(string text) { - switch (text) { - case "sng": - return eUnderLineType.Single; - case "dbl": - return eUnderLineType.Double; - case "": - return eUnderLineType.None; - default: - return Enum.TryParse<eUnderLineType>(text, true, out var result) - ? result - : eUnderLineType.None; - } - } - - private string TranslateUnderlineText(eUnderLineType value) { - switch (value) { - case eUnderLineType.Single: - return "sng"; - case eUnderLineType.Double: - return "dbl"; - default: - string ret = value.ToString(); - return ret.Substring(0, 1).ToLower(CultureInfo.InvariantCulture) - + ret.Substring(1, ret.Length - 1); - } - } - - private eStrikeType TranslateStrike(string text) { - switch (text) { - case "dblStrike": - return eStrikeType.Double; - case "sngStrike": - return eStrikeType.Single; - default: - return eStrikeType.No; - } - } - - private string TranslateStrikeText(eStrikeType value) { - switch (value) { - case eStrikeType.Single: - return "sngStrike"; - case eStrikeType.Double: - return "dblStrike"; - default: - return "noStrike"; - } - } -}
diff --git a/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs b/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs index 6e5d10d..c67aed9 100644 --- a/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs +++ b/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs
@@ -357,11 +357,8 @@ internal string NetFormatForWidth { get; private set; } - //internal string FractionFormatInteger { get; private set; } internal string FractionFormat { get; private set; } - //internal string FractionFormat2 { get; private set; } - private void ToNetFormat(string excelFormat, bool forColWidth) { DataType = eFormatType.Unknown; int secCount = 0; @@ -372,7 +369,6 @@ bool useMinute = false; bool prevUnderScore = false; bool ignoreNext = false; - int fractionPos = -1; string specialDateFormat = ""; bool containsAmPm = excelFormat.Contains("AM/PM"); List<int> lstDec = new List<int>(); @@ -511,7 +507,6 @@ sb.Append(' '); } else if (c == '/') { if (DataType == eFormatType.Number) { - fractionPos = sb.Length; int startPos = pos - 1; while (startPos >= 0 && (excelFormat[startPos] == '?'
diff --git a/EPPlus/Table/ExcelTable.cs b/EPPlus/Table/ExcelTable.cs index 2438154..674bb73 100644 --- a/EPPlus/Table/ExcelTable.cs +++ b/EPPlus/Table/ExcelTable.cs
@@ -32,9 +32,7 @@ using System; using System.Collections.Generic; -using System.Security; -using System.Text; -using System.Text.RegularExpressions; +using System.Collections.Immutable; using System.Xml; using OfficeOpenXml.Packaging; using OfficeOpenXml.Utils; @@ -113,101 +111,37 @@ /// An Excel Table /// </summary> public class ExcelTable : XmlHelper, IEqualityComparer<ExcelTable> { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "autoFilter", + "tableColumns", + "tableStyleInfo", + ]; + internal ExcelTable(ZipPackageRelationship rel, ExcelWorksheet sheet) : base(sheet.NameSpaceManager) { WorkSheet = sheet; TableUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri); RelationshipID = rel.Id; - var pck = sheet._package.Package; - Part = pck.GetPart(TableUri); - - TableXml = new(); - LoadXmlSafe(TableXml, Part.GetStream()); - Init(); + TableXml = sheet._package.GetXmlDocument(TableUri); + TopNode = TableXml.DocumentElement; Address = new(GetXmlNodeString("@ref")); } - internal ExcelTable(ExcelWorksheet sheet, ExcelAddressBase address, string name, int tblId) - : base(sheet.NameSpaceManager) { - WorkSheet = sheet; - Address = address; - TableXml = new(); - LoadXmlSafe(TableXml, GetStartXml(name, tblId), Encoding.UTF8); - TopNode = TableXml.DocumentElement; - - Init(); - - //If the table is just one row we can not have a header. - if (address._fromRow == address._toRow) { - ShowHeader = false; - } - } - - private void Init() { - TopNode = TableXml.DocumentElement; - SchemaNodeOrder = new[] { "autoFilter", "tableColumns", "tableStyleInfo" }; - } - - private string GetStartXml(string name, int tblId) { - string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>"; - xml += string.Format( - "<table xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" id=\"{0}\" name=\"{1}\" displayName=\"{2}\" ref=\"{3}\" headerRowCount=\"1\">", - tblId, - name, - CleanDisplayName(name), - Address.Address); - xml += string.Format("<autoFilter ref=\"{0}\" />", Address.Address); - - int cols = Address._toCol - Address._fromCol + 1; - xml += string.Format("<tableColumns count=\"{0}\">", cols); - var names = new Dictionary<string, string>(); - for (int i = 1; i <= cols; i++) { - var cell = WorkSheet.Cells[Address._fromRow, Address._fromCol + i - 1]; - string colName; - if (cell.Value == null || names.ContainsKey(cell.Value.ToString())) { - //Get an unique name - int a = i; - do { - colName = string.Format("Column{0}", a++); - } while (names.ContainsKey(colName)); - } else { - colName = SecurityElement.Escape(cell.Value.ToString()); - } - names.Add(colName, colName); - xml += string.Format("<tableColumn id=\"{0}\" name=\"{1}\" />", i, colName); - } - xml += "</tableColumns>"; - xml += - "<tableStyleInfo name=\"TableStyleMedium9\" showFirstColumn=\"0\" showLastColumn=\"0\" showRowStripes=\"1\" showColumnStripes=\"0\" /> "; - xml += "</table>"; - - return xml; - } - - private string CleanDisplayName(string name) { - return Regex.Replace(name, @"[^\w\.-_]", "_"); - } - - internal ZipPackagePart Part { get; set; } - /// <summary> /// Provides access to the XML data representing the table in the package. /// </summary> - public XmlDocument TableXml { get; set; } + internal XmlDocument TableXml { get; } /// <summary> /// The package internal URI to the Table Xml Document. /// </summary> - public Uri TableUri { get; internal set; } + private Uri TableUri { get; } internal string RelationshipID { get; set; } private const string _idPath = "@id"; - internal int Id { - get => GetXmlNodeInt(_idPath); - set => SetXmlNodeString(_idPath, value.ToString()); - } + internal int Id => GetXmlNodeInt(_idPath); private const string _namePath = "@name"; private const string _displayNamePath = "@displayName"; @@ -215,27 +149,12 @@ /// <summary> /// The name of the table object in Excel /// </summary> - public string Name { - get => GetXmlNodeString(_namePath); - set { - if (WorkSheet.Workbook.ExistsTableName(value)) { - throw (new ArgumentException("Tablename is not unique")); - } - string prevName = Name; - if (WorkSheet.Tables._tableNames.ContainsKey(prevName)) { - int ix = WorkSheet.Tables._tableNames[prevName]; - WorkSheet.Tables._tableNames.Remove(prevName); - WorkSheet.Tables._tableNames.Add(value, ix); - } - SetXmlNodeString(_namePath, value); - SetXmlNodeString(_displayNamePath, CleanDisplayName(value)); - } - } + public string Name => GetXmlNodeString(_namePath); /// <summary> /// The worksheet of the table /// </summary> - public ExcelWorksheet WorkSheet { get; set; } + public ExcelWorksheet WorkSheet { get; } private ExcelAddressBase _address; @@ -265,21 +184,6 @@ } } - private TableStyles _tableStyle = TableStyles.Medium6; - - /// <summary> - /// The table style. If this property is cusom, the style from the StyleName propery is used. - /// </summary> - public TableStyles TableStyle { - get => _tableStyle; - set { - _tableStyle = value; - if (value != TableStyles.Custom) { - SetXmlNodeString(_stylenamePath, "TableStyle" + value); - } - } - } - private const string _headerrowcountPath = "@headerRowCount"; private const string _autofilterPath = "d:autoFilter/@ref"; @@ -341,21 +245,7 @@ /// <summary> /// If the header row has an autofilter /// </summary> - public bool ShowFilter { - get => ShowHeader && AutoFilterAddress != null; - set { - if (ShowHeader) { - if (value) { - WriteAutoFilter(ShowTotal); - } else { - DeleteAllNode(_autofilterPath); - } - } else if (value) { - throw (new InvalidOperationException( - "Filter can only be applied when ShowHeader is set to true")); - } - } - } + public bool ShowFilter => ShowHeader && AutoFilterAddress != null; private const string _totalsrowcountPath = "@totalsRowCount"; @@ -399,131 +289,56 @@ /// <summary> /// The style name for custum styles /// </summary> - public string StyleName { - get => GetXmlNodeString(_stylenamePath); - set { - if (value.StartsWith("TableStyle")) { - try { - _tableStyle = (TableStyles) - Enum.Parse(typeof(TableStyles), value.Substring(10, value.Length - 10), true); - } catch { - _tableStyle = TableStyles.Custom; - } - } else if (value == "None") { - _tableStyle = TableStyles.None; - value = ""; - } else { - _tableStyle = TableStyles.Custom; - } - SetXmlNodeString(_stylenamePath, value, true); - } - } + public string StyleName => GetXmlNodeString(_stylenamePath); private const string _showfirstcolumnPath = "d:tableStyleInfo/@showFirstColumn"; /// <summary> /// Display special formatting for the first row /// </summary> - public bool ShowFirstColumn { - get => GetXmlNodeBool(_showfirstcolumnPath); - set => SetXmlNodeBool(_showfirstcolumnPath, value, false); - } + public bool ShowFirstColumn => GetXmlNodeBool(_showfirstcolumnPath); private const string _showlastcolumnPath = "d:tableStyleInfo/@showLastColumn"; /// <summary> /// Display special formatting for the last row /// </summary> - public bool ShowLastColumn { - get => GetXmlNodeBool(_showlastcolumnPath); - set => SetXmlNodeBool(_showlastcolumnPath, value, false); - } + public bool ShowLastColumn => GetXmlNodeBool(_showlastcolumnPath); private const string _showrowstripesPath = "d:tableStyleInfo/@showRowStripes"; /// <summary> /// Display banded rows /// </summary> - public bool ShowRowStripes { - get => GetXmlNodeBool(_showrowstripesPath); - set => SetXmlNodeBool(_showrowstripesPath, value, false); - } + public bool ShowRowStripes => GetXmlNodeBool(_showrowstripesPath); private const string _showcolumnstripesPath = "d:tableStyleInfo/@showColumnStripes"; /// <summary> /// Display banded columns /// </summary> - public bool ShowColumnStripes { - get => GetXmlNodeBool(_showcolumnstripesPath); - set => SetXmlNodeBool(_showcolumnstripesPath, value, false); - } + public bool ShowColumnStripes => GetXmlNodeBool(_showcolumnstripesPath); private const string _totalsrowcellstylePath = "@totalsRowCellStyle"; /// <summary> /// Named style used for the total row /// </summary> - public string TotalsRowCellStyle { - get => GetXmlNodeString(_totalsrowcellstylePath); - set { - if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) { - throw (new(string.Format("Named style {0} does not exist.", value))); - } - SetXmlNodeString(TopNode, _totalsrowcellstylePath, value, true); - - if (ShowTotal) { - WorkSheet.Cells[Address._toRow, - Address._fromCol, - Address._toRow, - Address._toCol].StyleName = value; - } - } - } + public string TotalsRowCellStyle => GetXmlNodeString(_totalsrowcellstylePath); private const string _datacellstylePath = "@dataCellStyle"; /// <summary> /// Named style used for the data cells /// </summary> - public string DataCellStyleName { - get => GetXmlNodeString(_datacellstylePath); - set { - if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) { - throw (new(string.Format("Named style {0} does not exist.", value))); - } - SetXmlNodeString(TopNode, _datacellstylePath, value, true); - - int fromRow = Address._fromRow + (ShowHeader ? 1 : 0), - toRow = Address._toRow - (ShowTotal ? 1 : 0); - - if (fromRow < toRow) { - WorkSheet.Cells[fromRow, Address._fromCol, toRow, Address._toCol].StyleName = value; - } - } - } + public string DataCellStyleName => GetXmlNodeString(_datacellstylePath); private const string _headerrowcellstylePath = "@headerRowCellStyle"; /// <summary> /// Named style used for the header row /// </summary> - public string HeaderRowCellStyle { - get => GetXmlNodeString(_headerrowcellstylePath); - set { - if (WorkSheet.Workbook.Styles.NamedStyles.FindIndexById(value) < 0) { - throw (new(string.Format("Named style {0} does not exist.", value))); - } - SetXmlNodeString(TopNode, _headerrowcellstylePath, value, true); - - if (ShowHeader) { - WorkSheet.Cells[Address._fromRow, - Address._fromCol, - Address._fromRow, - Address._toCol].StyleName = value; - } - } - } + public string HeaderRowCellStyle => GetXmlNodeString(_headerrowcellstylePath); public bool Equals(ExcelTable x, ExcelTable y) { return x.WorkSheet == y.WorkSheet && x.Id == y.Id && x.TableXml.OuterXml == y.TableXml.OuterXml;
diff --git a/EPPlus/Table/ExcelTableCollection.cs b/EPPlus/Table/ExcelTableCollection.cs index 75c255d..5a7941c 100644 --- a/EPPlus/Table/ExcelTableCollection.cs +++ b/EPPlus/Table/ExcelTableCollection.cs
@@ -58,95 +58,6 @@ } } - private ExcelTable Add(ExcelTable tbl) { - _tables.Add(tbl); - _tableNames.Add(tbl.Name, _tables.Count - 1); - if (tbl.Id >= _ws.Workbook._nextTableID) { - _ws.Workbook._nextTableID = tbl.Id + 1; - } - return tbl; - } - - /// <summary> - /// Create a table on the supplied range - /// </summary> - /// <param name="range">The range address including header and total row</param> - /// <param name="name">The name of the table. Must be unique </param> - /// <returns>The table object</returns> - public ExcelTable Add(ExcelAddressBase range, string name) { - if (range.WorkSheet != null && range.WorkSheet != _ws.Name) { - throw new ArgumentException("Range does not belong to worksheet", "range"); - } - - if (string.IsNullOrEmpty(name)) { - name = GetNewTableName(); - } else if (_ws.Workbook.ExistsTableName(name)) { - throw (new ArgumentException("Tablename is not unique")); - } - - ValidateTableName(name); - - foreach (var t in _tables) { - if (t.Address.Collide(range) != ExcelAddressBase.eAddressCollition.No) { - throw (new ArgumentException(string.Format("Table range collides with table {0}", t.Name))); - } - } - return Add(new(_ws, range, name, _ws.Workbook._nextTableID)); - } - - private void ValidateTableName(string name) { - if (string.IsNullOrEmpty(name)) { - throw new ArgumentException("Tablename is null or empty"); - } - - char firstLetterOfName = name[0]; - if (Char.IsLetter(firstLetterOfName) == false - && firstLetterOfName != '_' - && firstLetterOfName != '\\') { - throw new ArgumentException("Tablename start with invalid character"); - } - - if (name.Contains(" ")) { - throw new ArgumentException("Tablename has spaces"); - } - } - - public void Delete(int index, bool clearRange = false) { - Delete(this[index], clearRange); - } - - public void Delete(string name, bool clearRange = false) { - if (this[name] == null) { - throw new ArgumentOutOfRangeException( - string.Format("Cannot delete non-existant table {0} in sheet {1}.", name, _ws.Name)); - } - Delete(this[name], clearRange); - } - - public void Delete(ExcelTable excelTable, bool clearRange = false) { - if (!_tables.Contains(excelTable)) { - throw new ArgumentOutOfRangeException( - "excelTable", - String.Format("Table {0} does not exist in this collection", excelTable.Name)); - } - lock (this) { - var range = _ws.Cells[excelTable.Address.Address]; - _tableNames.Remove(excelTable.Name); - _tables.Remove(excelTable); - foreach (var sheet in excelTable.WorkSheet.Workbook.Worksheets) { - foreach (var table in sheet.Tables) { - if (table.Id > excelTable.Id) { - table.Id--; - } - } - excelTable.WorkSheet.Workbook._nextTableID--; - } - if (clearRange) { - range.Clear(); - } - } - } - internal string GetNewTableName() { string name = "Table1"; int i = 2;
diff --git a/EPPlus/Table/ExcelTableColumnCollection.cs b/EPPlus/Table/ExcelTableColumnCollection.cs index 57d59d8..e74adbe 100644 --- a/EPPlus/Table/ExcelTableColumnCollection.cs +++ b/EPPlus/Table/ExcelTableColumnCollection.cs
@@ -104,7 +104,7 @@ internal string GetUniqueName(string name) { if (_colNames.ContainsKey(name)) { - var newName = name; + string newName; var i = 2; do { newName = name + (i++).ToString(CultureInfo.InvariantCulture);
diff --git a/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs b/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs index d25001f..44543ee 100644 --- a/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs +++ b/EPPlus/Table/PivotTable/ExcelPivotCacheDefinition.cs
@@ -32,7 +32,6 @@ using System; using System.Linq; -using System.Text; using System.Xml; using OfficeOpenXml.Packaging; using OfficeOpenXml.Utils; @@ -75,7 +74,6 @@ var pck = pivotTable.WorkSheet._package.Package; Part = pck.GetPart(CacheDefinitionUri); - CacheDefinitionXml = new(); LoadXmlSafe(CacheDefinitionXml, Part.GetStream()); TopNode = CacheDefinitionXml.DocumentElement; @@ -91,40 +89,6 @@ } } - internal ExcelPivotCacheDefinition( - XmlNamespaceManager ns, - ExcelPivotTable pivotTable, - ExcelRangeBase sourceAddress, - int tblId) - : base(ns, null) { - PivotTable = pivotTable; - - var pck = pivotTable.WorkSheet._package.Package; - - //CacheDefinition - CacheDefinitionXml = new(); - LoadXmlSafe(CacheDefinitionXml, GetStartXml(sourceAddress), Encoding.UTF8); - CacheDefinitionUri = GetNewUri(pck, "/xl/pivotCache/pivotCacheDefinition{0}.xml", tblId); - Part = pck.CreatePart(CacheDefinitionUri, ExcelPackage._schemaPivotCacheDefinition); - TopNode = CacheDefinitionXml.DocumentElement; - - //CacheRecord. Create an empty one. - CacheRecordUri = GetNewUri(pck, "/xl/pivotCache/pivotCacheRecords{0}.xml", tblId); - var cacheRecord = new XmlDocument(); - cacheRecord.LoadXml( - "<pivotCacheRecords xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" count=\"0\" />"); - var recPart = pck.CreatePart(CacheRecordUri, ExcelPackage._schemaPivotCacheRecords); - cacheRecord.Save(recPart.GetStream()); - - RecordRelationship = Part.CreateRelationship( - UriHelper.ResolvePartUri(CacheDefinitionUri, CacheRecordUri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/pivotCacheRecords"); - RecordRelationshipID = RecordRelationship.Id; - - CacheDefinitionXml.Save(Part.GetStream()); - } - /// <summary> /// Reference to the internal package part /// </summary> @@ -133,7 +97,7 @@ /// <summary> /// Provides access to the XML data representing the cache definition in the package. /// </summary> - public XmlDocument CacheDefinitionXml { get; private set; } + public XmlDocument CacheDefinitionXml { get; private set; } = new(); /// <summary> /// The package internal URI to the pivottable cache definition Xml Document.
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTable.cs b/EPPlus/Table/PivotTable/ExcelPivotTable.cs index 6ea4244..47031ca 100644 --- a/EPPlus/Table/PivotTable/ExcelPivotTable.cs +++ b/EPPlus/Table/PivotTable/ExcelPivotTable.cs
@@ -31,7 +31,7 @@ *******************************************************************************/ using System; -using System.Text; +using System.Collections.Immutable; using System.Text.RegularExpressions; using System.Xml; using OfficeOpenXml.Packaging; @@ -43,21 +43,32 @@ /// An Excel Pivottable /// </summary> public class ExcelPivotTable : XmlHelper { + protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ + "location", + "pivotFields", + "rowFields", + "rowItems", + "colFields", + "colItems", + "pageFields", + "pageItems", + "dataFields", + "dataItems", + "formats", + "pivotTableStyleInfo", + ]; + internal ExcelPivotTable(ZipPackageRelationship rel, ExcelWorksheet sheet) : base(sheet.NameSpaceManager) { WorkSheet = sheet; PivotTableUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri); Relationship = rel; - var pck = sheet._package.Package; - Part = pck.GetPart(PivotTableUri); - - PivotTableXml = new(); - LoadXmlSafe(PivotTableXml, Part.GetStream()); - Init(); + var package = sheet._package; + PivotTableXml = package.GetXmlDocument(PivotTableUri); TopNode = PivotTableXml.DocumentElement; Address = new(GetXmlNodeString("d:location/@ref")); - _cacheDefinition = new(sheet.NameSpaceManager, this); + CacheDefinition = new(sheet.NameSpaceManager, this); LoadFields(); //Add row fields. @@ -103,72 +114,6 @@ } } - /// <summary> - /// Add a new pivottable - /// </summary> - /// <param name="sheet">The worksheet</param> - /// <param name="address">the address of the pivottable</param> - /// <param name="sourceAddress">The address of the Source data</param> - /// <param name="name"></param> - /// <param name="tblId"></param> - internal ExcelPivotTable( - ExcelWorksheet sheet, - ExcelAddressBase address, - ExcelRangeBase sourceAddress, - string name, - int tblId) - : base(sheet.NameSpaceManager) { - WorkSheet = sheet; - Address = address; - var pck = sheet._package.Package; - - PivotTableXml = new(); - LoadXmlSafe(PivotTableXml, GetStartXml(name, tblId, address, sourceAddress), Encoding.UTF8); - TopNode = PivotTableXml.DocumentElement; - PivotTableUri = GetNewUri(pck, "/xl/pivotTables/pivotTable{0}.xml", tblId); - Init(); - - Part = pck.CreatePart(PivotTableUri, ExcelPackage._schemaPivotTable); - PivotTableXml.Save(Part.GetStream()); - - //Worksheet-Pivottable relationship - Relationship = sheet.Part.CreateRelationship( - UriHelper.ResolvePartUri(sheet.WorksheetUri, PivotTableUri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/pivotTable"); - - _cacheDefinition = new(sheet.NameSpaceManager, this, sourceAddress, tblId); - _cacheDefinition.Relationship = Part.CreateRelationship( - UriHelper.ResolvePartUri(PivotTableUri, _cacheDefinition.CacheDefinitionUri), - TargetMode.Internal, - ExcelPackage._schemaRelationships + "/pivotCacheDefinition"); - - sheet.Workbook.AddPivotTable(CacheID.ToString(), _cacheDefinition.CacheDefinitionUri); - - LoadFields(); - - using (var r = sheet.Cells[address.Address]) { - r.Clear(); - } - } - - private void Init() { - SchemaNodeOrder = new[] { - "location", - "pivotFields", - "rowFields", - "rowItems", - "colFields", - "colItems", - "pageFields", - "pageItems", - "dataFields", - "dataItems", - "formats", - "pivotTableStyleInfo", - }; - } - private void LoadFields() { //Fields.Clear(); //int ix=0; @@ -188,7 +133,7 @@ //Add fields. index = 0; - foreach (XmlElement fieldElem in _cacheDefinition.TopNode.SelectNodes( + foreach (XmlElement fieldElem in CacheDefinition.TopNode.SelectNodes( "d:cacheFields/d:cacheField", NameSpaceManager)) { var fld = Fields[index++]; @@ -196,34 +141,34 @@ } } - private string GetStartXml( + private XmlDocument GetStartXml( string name, int id, ExcelAddressBase address, ExcelAddressBase sourceAddress) { - string xml = string.Format( - "<pivotTableDefinition xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" name=\"{0}\" cacheId=\"{1}\" dataOnRows=\"1\" applyNumberFormats=\"0\" applyBorderFormats=\"0\" applyFontFormats=\"0\" applyPatternFormats=\"0\" applyAlignmentFormats=\"0\" applyWidthHeightFormats=\"1\" dataCaption=\"Data\" createdVersion=\"4\" showMemberPropertyTips=\"0\" useAutoFormatting=\"1\" itemPrintTitles=\"1\" indent=\"0\" compact=\"0\" compactData=\"0\" gridDropZones=\"1\">", - name, - id); + string xml = $""" + <pivotTableDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" name=" + {name}" cacheId="{id + }" dataOnRows="1" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="Data" createdVersion="4" showMemberPropertyTips="0" useAutoFormatting="1" itemPrintTitles="1" indent="0" compact="0" compactData="0" gridDropZones="1"> - xml += string.Format( - "<location ref=\"{0}\" firstHeaderRow=\"1\" firstDataRow=\"1\" firstDataCol=\"1\" /> ", - address.FirstAddress); - xml += string.Format( - "<pivotFields count=\"{0}\">", - sourceAddress._toCol - sourceAddress._fromCol + 1); + """; + xml += + $"""<location ref="{address.FirstAddress + }" firstHeaderRow="1" firstDataRow="1" firstDataCol="1" />"""; + xml += $"""<pivotFields count="{sourceAddress._toCol - sourceAddress._fromCol + 1}">"""; for (int col = sourceAddress._fromCol; col <= sourceAddress._toCol; col++) { - xml += "<pivotField showAll=\"0\" />"; //compact=\"0\" outline=\"0\" subtotalTop=\"0\" includeNewItemsInFilter=\"1\" + xml += """<pivotField showAll="0" />"""; } - xml += "</pivotFields>"; xml += - "<pivotTableStyleInfo name=\"PivotStyleMedium9\" showRowHeaders=\"1\" showColHeaders=\"1\" showRowStripes=\"0\" showColStripes=\"0\" showLastColumn=\"1\" />"; + """<pivotTableStyleInfo name="PivotStyleMedium9" showRowHeaders="1" showColHeaders="1" showRowStripes="0" showColStripes="0" showLastColumn="1" />"""; xml += "</pivotTableDefinition>"; - return xml; + var result = new XmlDocument(); + result.LoadXml(xml); + return result; } - internal ZipPackagePart Part { get; set; } + internal ZipPackagePart Part { get; } /// <summary> /// Provides access to the XML data representing the pivottable in the package. @@ -272,19 +217,10 @@ } } - private ExcelPivotCacheDefinition _cacheDefinition; - /// <summary> /// Reference to the pivot table cache definition object /// </summary> - public ExcelPivotCacheDefinition CacheDefinition { - get { - if (_cacheDefinition == null) { - _cacheDefinition = new(NameSpaceManager, this, null, 1); - } - return _cacheDefinition; - } - } + public ExcelPivotCacheDefinition CacheDefinition { get; } private string CleanDisplayName(string name) { return Regex.Replace(name, @"[^\w\.-_]", "_");
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs b/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs index c6c918f..fe9cc63 100644 --- a/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs +++ b/EPPlus/Table/PivotTable/ExcelPivotTableCollection.cs
@@ -41,12 +41,9 @@ /// </summary> public class ExcelPivotTableCollection : IEnumerable<ExcelPivotTable> { private readonly List<ExcelPivotTable> _pivotTables = new(); - internal Dictionary<string, int> _pivotTableNames = new(); - private readonly ExcelWorksheet _ws; + internal readonly Dictionary<string, int> _pivotTableNames = new(); internal ExcelPivotTableCollection(ExcelWorksheet ws) { - var pck = ws._package.Package; - _ws = ws; foreach (var rel in ws.Part.GetRelationships()) { if (rel.RelationshipType == ExcelPackage._schemaRelationships + "/pivotTable") { var tbl = new ExcelPivotTable(rel, ws); @@ -56,49 +53,6 @@ } } - private ExcelPivotTable Add(ExcelPivotTable tbl) { - _pivotTables.Add(tbl); - _pivotTableNames.Add(tbl.Name, _pivotTables.Count - 1); - if (tbl.CacheID >= _ws.Workbook._nextPivotTableID) { - _ws.Workbook._nextPivotTableID = tbl.CacheID + 1; - } - return tbl; - } - - /// <summary> - /// Create a pivottable on the supplied range - /// </summary> - /// <param name="range">The range address including header and total row</param> - /// <param name="source">The Source data range address</param> - /// <param name="name">The name of the table. Must be unique </param> - /// <returns>The pivottable object</returns> - public ExcelPivotTable Add(ExcelAddressBase range, ExcelRangeBase source, string name) { - if (string.IsNullOrEmpty(name)) { - name = GetNewTableName(); - } - if (range.WorkSheet != _ws.Name) { - throw (new("The Range must be in the current worksheet")); - } - if (_ws.Workbook.ExistsTableName(name)) { - throw (new ArgumentException("Tablename is not unique")); - } - foreach (var t in _pivotTables) { - if (t.Address.Collide(range) != ExcelAddressBase.eAddressCollition.No) { - throw (new ArgumentException(string.Format("Table range collides with table {0}", t.Name))); - } - } - return Add(new(_ws, range, source, name, _ws.Workbook._nextPivotTableID++)); - } - - internal string GetNewTableName() { - string name = "Pivottable1"; - int i = 2; - while (_ws.Workbook.ExistsPivotTableName(name)) { - name = string.Format("Pivottable{0}", i++); - } - return name; - } - public int Count => _pivotTables.Count; /// <summary>
diff --git a/EPPlus/Table/PivotTable/ExcelPivotTableField.cs b/EPPlus/Table/PivotTable/ExcelPivotTableField.cs index 9869a11..05570f2 100644 --- a/EPPlus/Table/PivotTable/ExcelPivotTableField.cs +++ b/EPPlus/Table/PivotTable/ExcelPivotTableField.cs
@@ -31,7 +31,6 @@ *******************************************************************************/ using System; -using System.Collections.Generic; using System.Globalization; using System.Xml; @@ -545,221 +544,6 @@ } } - internal ExcelPivotTableFieldDateGroup SetDateGroup( - eDateGroupBy groupBy, - DateTime startDate, - DateTime endDate, - int interval) { - ExcelPivotTableFieldDateGroup group; - group = new(NameSpaceManager, _cacheFieldHelper.TopNode); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsDate", true); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsNonDate", false); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsSemiMixedTypes", false); - - group.TopNode.InnerXml += string.Format( - "<fieldGroup base=\"{0}\"><rangePr groupBy=\"{1}\" /><groupItems /></fieldGroup>", - BaseIndex, - groupBy.ToString().ToLower(CultureInfo.InvariantCulture)); - - if (startDate.Year < 1900) { - _cacheFieldHelper.SetXmlNodeString( - "d:fieldGroup/d:rangePr/@startDate", - "1900-01-01T00:00:00"); - } else { - _cacheFieldHelper.SetXmlNodeString( - "d:fieldGroup/d:rangePr/@startDate", - startDate.ToString("s", CultureInfo.InvariantCulture)); - _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@autoStart", "0"); - } - - if (endDate == DateTime.MaxValue) { - _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@endDate", "9999-12-31T00:00:00"); - } else { - _cacheFieldHelper.SetXmlNodeString( - "d:fieldGroup/d:rangePr/@endDate", - endDate.ToString("s", CultureInfo.InvariantCulture)); - _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/d:rangePr/@autoEnd", "0"); - } - - int items = AddDateGroupItems(group, groupBy, startDate, endDate, interval); - AddFieldItems(items); - - _grouping = group; - return group; - } - - internal ExcelPivotTableFieldNumericGroup SetNumericGroup( - double start, - double end, - double interval) { - ExcelPivotTableFieldNumericGroup group; - group = new(NameSpaceManager, _cacheFieldHelper.TopNode); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsNumber", true); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsInteger", true); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsSemiMixedTypes", false); - _cacheFieldHelper.SetXmlNodeBool("d:sharedItems/@containsString", false); - - group.TopNode.InnerXml += string.Format( - "<fieldGroup base=\"{0}\"><rangePr autoStart=\"0\" autoEnd=\"0\" startNum=\"{1}\" endNum=\"{2}\" groupInterval=\"{3}\"/><groupItems /></fieldGroup>", - BaseIndex, - start.ToString(CultureInfo.InvariantCulture), - end.ToString(CultureInfo.InvariantCulture), - interval.ToString(CultureInfo.InvariantCulture)); - int items = AddNumericGroupItems(group, start, end, interval); - AddFieldItems(items); - - _grouping = group; - return group; - } - - private int AddNumericGroupItems( - ExcelPivotTableFieldNumericGroup group, - double start, - double end, - double interval) { - if (interval < 0) { - throw (new("The interval must be a positiv")); - } - if (start > end) { - throw (new("Then End number must be larger than the Start number")); - } - - XmlElement groupItems = - group.TopNode.SelectSingleNode("d:fieldGroup/d:groupItems", group.NameSpaceManager) - as XmlElement; - int items = 2; - //First date - double index = start; - double nextIndex = start + interval; - AddGroupItem(groupItems, "<" + start.ToString(CultureInfo.InvariantCulture)); - - while (index < end) { - AddGroupItem( - groupItems, - string.Format( - "{0}-{1}", - index.ToString(CultureInfo.InvariantCulture), - nextIndex.ToString(CultureInfo.InvariantCulture))); - index = nextIndex; - nextIndex += interval; - items++; - } - AddGroupItem(groupItems, ">" + nextIndex.ToString(CultureInfo.InvariantCulture)); - return items; - } - - private void AddFieldItems(int items) { - XmlElement prevNode = null; - XmlElement itemsNode = TopNode.SelectSingleNode("d:items", NameSpaceManager) as XmlElement; - for (int x = 0; x < items; x++) { - var itemNode = itemsNode.OwnerDocument.CreateElement("item", ExcelPackage._schemaMain); - itemNode.SetAttribute("x", x.ToString()); - if (prevNode == null) { - itemsNode.PrependChild(itemNode); - } else { - itemsNode.InsertAfter(itemNode, prevNode); - } - prevNode = itemNode; - } - itemsNode.SetAttribute("count", (items + 1).ToString()); - } - - private int AddDateGroupItems( - ExcelPivotTableFieldGroup group, - eDateGroupBy groupBy, - DateTime startDate, - DateTime endDate, - int interval) { - XmlElement groupItems = - group.TopNode.SelectSingleNode("d:fieldGroup/d:groupItems", group.NameSpaceManager) - as XmlElement; - int items = 2; - //First date - AddGroupItem( - groupItems, - "<" + startDate.ToString("s", CultureInfo.InvariantCulture).Substring(0, 10)); - - switch (groupBy) { - case eDateGroupBy.Seconds: - case eDateGroupBy.Minutes: - AddTimeSerie(60, groupItems); - items += 60; - break; - case eDateGroupBy.Hours: - AddTimeSerie(24, groupItems); - items += 24; - break; - case eDateGroupBy.Days: - if (interval == 1) { - DateTime dt = new DateTime(2008, 1, 1); //pick a year with 366 days - while (dt.Year == 2008) { - AddGroupItem(groupItems, dt.ToString("dd-MMM")); - dt = dt.AddDays(1); - } - items += 366; - } else { - DateTime dt = startDate; - items = 0; - while (dt < endDate) { - AddGroupItem(groupItems, dt.ToString("dd-MMM")); - dt = dt.AddDays(interval); - items++; - } - } - break; - case eDateGroupBy.Months: - AddGroupItem(groupItems, "jan"); - AddGroupItem(groupItems, "feb"); - AddGroupItem(groupItems, "mar"); - AddGroupItem(groupItems, "apr"); - AddGroupItem(groupItems, "may"); - AddGroupItem(groupItems, "jun"); - AddGroupItem(groupItems, "jul"); - AddGroupItem(groupItems, "aug"); - AddGroupItem(groupItems, "sep"); - AddGroupItem(groupItems, "oct"); - AddGroupItem(groupItems, "nov"); - AddGroupItem(groupItems, "dec"); - items += 12; - break; - case eDateGroupBy.Quarters: - AddGroupItem(groupItems, "Qtr1"); - AddGroupItem(groupItems, "Qtr2"); - AddGroupItem(groupItems, "Qtr3"); - AddGroupItem(groupItems, "Qtr4"); - items += 4; - break; - case eDateGroupBy.Years: - if (startDate.Year >= 1900 && endDate != DateTime.MaxValue) { - for (int year = startDate.Year; year <= endDate.Year; year++) { - AddGroupItem(groupItems, year.ToString()); - } - items += endDate.Year - startDate.Year + 1; - } - break; - default: - throw (new("unsupported grouping")); - } - - //Lastdate - AddGroupItem( - groupItems, - ">" + endDate.ToString("s", CultureInfo.InvariantCulture).Substring(0, 10)); - return items; - } - - private void AddTimeSerie(int count, XmlElement groupItems) { - for (int i = 0; i < count; i++) { - AddGroupItem(groupItems, string.Format("{0:00}", i)); - } - } - - private void AddGroupItem(XmlElement groupItems, string value) { - var s = groupItems.OwnerDocument.CreateElement("s", ExcelPackage._schemaMain); - s.SetAttribute("v", value); - groupItems.AppendChild(s); - } - internal ExcelPivotTableFieldCollectionBase<ExcelPivotTableFieldItem> _items; /// <summary> @@ -775,236 +559,8 @@ _items.AddInternal(item); } } - //if (_grouping is ExcelPivotTableFieldDateGroup) - //{ - // ExcelPivotTableFieldDateGroup dtgrp = ((ExcelPivotTableFieldDateGroup)_grouping); - - // ExcelPivotTableFieldItem minItem=null, maxItem=null; - // foreach (var item in _items) - // { - // if (item.X == 0) - // { - // minItem = item; - // } - // else if (maxItem == null || maxItem.X < item.X) - // { - // maxItem = item; - // } - // } - // if (dtgrp.AutoStart) - // { - // _items._list.Remove(minItem); - // } - // if (dtgrp.AutoEnd) - // { - // _items._list.Remove(maxItem); - // } - - //} } return _items; } } - - /// <summary> - /// Add numberic grouping to the field - /// </summary> - /// <param name="start">Start value</param> - /// <param name="end">End value</param> - /// <param name="interval">Interval</param> - public void AddNumericGrouping(double start, double end, double interval) { - ValidateGrouping(); - SetNumericGroup(start, end, interval); - } - - /// <summary> - /// Add a date grouping on this field. - /// </summary> - /// <param name="groupBy">Group by</param> - public void AddDateGrouping(eDateGroupBy groupBy) { - AddDateGrouping(groupBy, DateTime.MinValue, DateTime.MaxValue, 1); - } - - /// <summary> - /// Add a date grouping on this field. - /// </summary> - /// <param name="groupBy">Group by</param> - /// <param name="startDate">Fixed start date. Use DateTime.MinValue for auto</param> - /// <param name="endDate">Fixed end date. Use DateTime.MaxValue for auto</param> - public void AddDateGrouping(eDateGroupBy groupBy, DateTime startDate, DateTime endDate) { - AddDateGrouping(groupBy, startDate, endDate, 1); - } - - /// <summary> - /// Add a date grouping on this field. - /// </summary> - /// <param name="days">Number of days when grouping on days</param> - /// <param name="startDate">Fixed start date. Use DateTime.MinValue for auto</param> - /// <param name="endDate">Fixed end date. Use DateTime.MaxValue for auto</param> - public void AddDateGrouping(int days, DateTime startDate, DateTime endDate) { - AddDateGrouping(eDateGroupBy.Days, startDate, endDate, days); - } - - private void AddDateGrouping( - eDateGroupBy groupBy, - DateTime startDate, - DateTime endDate, - int groupInterval) { - if (groupInterval < 1 || groupInterval >= Int16.MaxValue) { - throw (new ArgumentOutOfRangeException("Group interval is out of range")); - } - if (groupInterval > 1 && groupBy != eDateGroupBy.Days) { - throw (new ArgumentException("Group interval is can only be used when groupBy is Days")); - } - ValidateGrouping(); - - bool firstField = true; - List<ExcelPivotTableField> fields = new List<ExcelPivotTableField>(); - //Seconds - if ((groupBy & eDateGroupBy.Seconds) == eDateGroupBy.Seconds) { - fields.Add(AddField(eDateGroupBy.Seconds, startDate, endDate, ref firstField)); - } - //Minutes - if ((groupBy & eDateGroupBy.Minutes) == eDateGroupBy.Minutes) { - fields.Add(AddField(eDateGroupBy.Minutes, startDate, endDate, ref firstField)); - } - //Hours - if ((groupBy & eDateGroupBy.Hours) == eDateGroupBy.Hours) { - fields.Add(AddField(eDateGroupBy.Hours, startDate, endDate, ref firstField)); - } - //Days - if ((groupBy & eDateGroupBy.Days) == eDateGroupBy.Days) { - fields.Add(AddField(eDateGroupBy.Days, startDate, endDate, ref firstField, groupInterval)); - } - //Month - if ((groupBy & eDateGroupBy.Months) == eDateGroupBy.Months) { - fields.Add(AddField(eDateGroupBy.Months, startDate, endDate, ref firstField)); - } - //Quarters - if ((groupBy & eDateGroupBy.Quarters) == eDateGroupBy.Quarters) { - fields.Add(AddField(eDateGroupBy.Quarters, startDate, endDate, ref firstField)); - } - //Years - if ((groupBy & eDateGroupBy.Years) == eDateGroupBy.Years) { - fields.Add(AddField(eDateGroupBy.Years, startDate, endDate, ref firstField)); - } - - if (fields.Count > 1) { - _cacheFieldHelper.SetXmlNodeString("d:fieldGroup/@par", (_table.Fields.Count - 1).ToString()); - } - if (groupInterval != 1) { - _cacheFieldHelper.SetXmlNodeString( - "d:fieldGroup/d:rangePr/@groupInterval", - groupInterval.ToString()); - } else { - _cacheFieldHelper.DeleteNode("d:fieldGroup/d:rangePr/@groupInterval"); - } - _items = null; - } - - private void ValidateGrouping() { - if (!(IsColumnField || IsRowField)) { - throw (new("Field must be a row or column field")); - } - foreach (var field in _table.Fields) { - if (field.Grouping != null) { - throw (new("Grouping already exists")); - } - } - } - - private ExcelPivotTableField AddField( - eDateGroupBy groupBy, - DateTime startDate, - DateTime endDate, - ref bool firstField) { - return AddField(groupBy, startDate, endDate, ref firstField, 1); - } - - private ExcelPivotTableField AddField( - eDateGroupBy groupBy, - DateTime startDate, - DateTime endDate, - ref bool firstField, - int interval) { - if (firstField == false) { - //Pivot field - var topNode = _table.PivotTableXml.SelectSingleNode( - "//d:pivotFields", - _table.NameSpaceManager); - var fieldNode = _table.PivotTableXml.CreateElement("pivotField", ExcelPackage._schemaMain); - fieldNode.SetAttribute("compact", "0"); - fieldNode.SetAttribute("outline", "0"); - fieldNode.SetAttribute("showAll", "0"); - fieldNode.SetAttribute("defaultSubtotal", "0"); - topNode.AppendChild(fieldNode); - - var field = new ExcelPivotTableField( - _table.NameSpaceManager, - fieldNode, - _table, - _table.Fields.Count, - Index); - field.DateGrouping = groupBy; - - XmlNode rowColFields; - if (IsRowField) { - rowColFields = TopNode.SelectSingleNode("../../d:rowFields", NameSpaceManager); - } else { - rowColFields = TopNode.SelectSingleNode("../../d:colFields", NameSpaceManager); - } - - var index = 0; - foreach (XmlElement rowfield in rowColFields.ChildNodes) { - if (int.TryParse(rowfield.GetAttribute("x"), out var fieldIndex)) { - if (_table.Fields[fieldIndex].BaseIndex == BaseIndex) { - var newElement = rowColFields.OwnerDocument.CreateElement( - "field", - ExcelPackage._schemaMain); - newElement.SetAttribute("x", field.Index.ToString()); - rowColFields.InsertBefore(newElement, rowfield); - break; - } - } - index++; - } - - if (IsRowField) { - _table.RowFields.Insert(field, index); - } else { - _table.ColumnFields.Insert(field, index); - } - - _table.Fields.AddInternal(field); - - AddCacheField(field, startDate, endDate, interval); - return field; - } - firstField = false; - DateGrouping = groupBy; - Compact = false; - SetDateGroup(groupBy, startDate, endDate, interval); - return this; - } - - private void AddCacheField( - ExcelPivotTableField field, - DateTime startDate, - DateTime endDate, - int interval) { - //Add Cache definition field. - var cacheTopNode = _table.CacheDefinition.CacheDefinitionXml.SelectSingleNode( - "//d:cacheFields", - _table.NameSpaceManager); - var cacheFieldNode = _table.CacheDefinition.CacheDefinitionXml.CreateElement( - "cacheField", - ExcelPackage._schemaMain); - - cacheFieldNode.SetAttribute("name", field.DateGrouping.ToString()); - cacheFieldNode.SetAttribute("databaseField", "0"); - cacheTopNode.AppendChild(cacheFieldNode); - field.SetCacheFieldNode(cacheFieldNode); - - field.SetDateGroup(field.DateGrouping, startDate, endDate, interval); - } }
diff --git a/EPPlus/XmlHelper.cs b/EPPlus/XmlHelper.cs index a958baf..b311997 100644 --- a/EPPlus/XmlHelper.cs +++ b/EPPlus/XmlHelper.cs
@@ -33,11 +33,11 @@ *******************************************************************************/ using System; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Text; using System.Xml; -using OfficeOpenXml.Packaging; using OfficeOpenXml.Style; namespace OfficeOpenXml; @@ -64,15 +64,10 @@ internal XmlNode TopNode { get; set; } - private string[] _schemaNodeOrder; - /// <summary> /// Schema order list /// </summary> - internal string[] SchemaNodeOrder { - get => _schemaNodeOrder; - set => _schemaNodeOrder = value; - } + protected virtual ImmutableArray<string> SchemaNodeOrder => ImmutableArray<string>.Empty; internal XmlNode CreateNode(string path) { if (path == "") { @@ -90,10 +85,10 @@ string nodeName; string nodePrefix; - string nameSpaceUri = ""; + string nameSpaceUri; string[] nameSplit = subPath.Split(':'); - if (SchemaNodeOrder != null && subPath[0] != '@') { + if (SchemaNodeOrder.Length > 0 && subPath[0] != '@') { insertFirst = false; prependNode = GetPrependNode(subPath, node); } @@ -176,16 +171,6 @@ } /// <summary> - /// Create a complex node. Insert the node according to SchemaOrder - /// using the TopNode as the parent - /// </summary> - /// <param name="path"></param> - /// <returns></returns> - internal XmlNode CreateComplexNode(string path) { - return CreateComplexNode(TopNode, path, eNodeInsertOrder.SchemaOrder, null); - } - - /// <summary> /// Create a complex node. Insert the node according to the <paramref name="path"/> /// using the <paramref name="topNode"/> as the parent /// </summary> @@ -243,7 +228,6 @@ } XmlNode node = topNode; - string nameSpaceUri = string.Empty; //TODO: BUG: when the "path" contains "/" in an attrribue value, it gives an error. @@ -312,7 +296,7 @@ string nodeName; string nodePrefix; string[] nameSplit = subPath.Split(':'); - nameSpaceUri = string.Empty; + string nameSpaceUri; // Check if the name has a prefix like "d:nodeName" if (nameSplit.Length > 1) { @@ -347,7 +331,7 @@ // Check if we need to use the "SchemaOrder" if (nodeInsertOrder == eNodeInsertOrder.SchemaOrder) { // Check if the Schema Order List is empty - if ((SchemaNodeOrder == null) || (SchemaNodeOrder.Length == 0)) { + if (SchemaNodeOrder.Length == 0) { // Use the "Insert Last" option when Schema Order List is empty nodeInsertOrder = eNodeInsertOrder.Last; } else { @@ -428,8 +412,8 @@ if (ix > 0) { nodeName = nodeName.Substring(ix + 1, nodeName.Length - (ix + 1)); } - for (int i = 0; i < _schemaNodeOrder.Length; i++) { - if (nodeName == _schemaNodeOrder[i]) { + for (int i = 0; i < SchemaNodeOrder.Length; i++) { + if (nodeName == SchemaNodeOrder[i]) { return i; } } @@ -464,10 +448,6 @@ } } - internal void DeleteTopNode() { - TopNode.ParentNode.RemoveChild(TopNode); - } - internal void SetXmlNodeString(string path, string value) { SetXmlNodeString(TopNode, path, value, false, false); } @@ -476,10 +456,6 @@ SetXmlNodeString(TopNode, path, value, removeIfBlank, false); } - internal void SetXmlNodeString(XmlNode node, string path, string value) { - SetXmlNodeString(node, path, value, false, false); - } - internal void SetXmlNodeString(XmlNode node, string path, string value, bool removeIfBlank) { SetXmlNodeString(node, path, value, removeIfBlank, false); } @@ -637,37 +613,6 @@ return GetXmlNodeString(TopNode, path); } - internal static Uri GetNewUri(ZipPackage package, string sUri) { - return GetNewUri(package, sUri, 1); - } - - internal static Uri GetNewUri(ZipPackage package, string sUri, int id) { - Uri uri; - do { - uri = new(string.Format(sUri, id++), UriKind.Relative); - } while (package.PartExists(uri)); - return uri; - } - - /// <summary> - /// Insert the new node before any of the nodes in the comma separeted list - /// </summary> - /// <param name="parentNode">Parent node</param> - /// <param name="beforeNodes">comma separated list containing nodes to insert after. Left to right order</param> - /// <param name="newNode">The new node to be inserterd</param> - internal void InserAfter(XmlNode parentNode, string beforeNodes, XmlNode newNode) { - string[] nodePaths = beforeNodes.Split(','); - - foreach (string nodePath in nodePaths) { - XmlNode node = parentNode.SelectSingleNode(nodePath, NameSpaceManager); - if (node != null) { - parentNode.InsertAfter(newNode, node); - return; - } - } - parentNode.InsertAfter(newNode, null); - } - internal static void LoadXmlSafe(XmlDocument xmlDoc, Stream stream) { XmlReaderSettings settings = new XmlReaderSettings(); //Disable entity parsing (to aviod xmlbombs, External Entity Attacks etc).