|  | /******************************************************************************* | 
|  | * 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		        2011-11-02 | 
|  | * Jan Källman          Total rewrite               2010-03-01 | 
|  | * Jan Källman		    License changed GPL-->LGPL  2011-12-27 | 
|  | *******************************************************************************/ | 
|  | using System; | 
|  | using System.Xml; | 
|  | using System.Collections.Generic; | 
|  | using System.IO; | 
|  | using System.Configuration; | 
|  | using OfficeOpenXml.Drawing; | 
|  | using System.Diagnostics; | 
|  | using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; | 
|  | using OfficeOpenXml.Style; | 
|  | using System.Globalization; | 
|  | using System.Text; | 
|  | using System.Security; | 
|  | using OfficeOpenXml.Drawing.Chart; | 
|  | using OfficeOpenXml.Style.XmlAccess; | 
|  | using System.Text.RegularExpressions; | 
|  | using OfficeOpenXml.Drawing.Vml; | 
|  | using OfficeOpenXml.Table; | 
|  | using OfficeOpenXml.DataValidation; | 
|  | using OfficeOpenXml.Table.PivotTable; | 
|  | using System.ComponentModel; | 
|  | using System.Drawing; | 
|  | using OfficeOpenXml.ConditionalFormatting; | 
|  | using OfficeOpenXml.Utils; | 
|  | using Ionic.Zip; | 
|  | using OfficeOpenXml.FormulaParsing.LexicalAnalysis; | 
|  | using OfficeOpenXml.FormulaParsing; | 
|  | using OfficeOpenXml.Packaging.Ionic.Zip; | 
|  | namespace OfficeOpenXml | 
|  | { | 
|  | /// <summary> | 
|  | /// Worksheet hidden enumeration | 
|  | /// </summary> | 
|  | public enum eWorkSheetHidden | 
|  | { | 
|  | /// <summary> | 
|  | /// The worksheet is visible | 
|  | /// </summary> | 
|  | Visible, | 
|  | /// <summary> | 
|  | /// The worksheet is hidden but can be shown by the user via the user interface | 
|  | /// </summary> | 
|  | Hidden, | 
|  | /// <summary> | 
|  | /// The worksheet is hidden and cannot be shown by the user via the user interface | 
|  | /// </summary> | 
|  | VeryHidden | 
|  | } | 
|  | [Flags] | 
|  | internal enum CellFlags | 
|  | { | 
|  | //Merged = 0x1, | 
|  | RichText = 0x2, | 
|  | SharedFormula = 0x4, | 
|  | ArrayFormula = 0x8 | 
|  | } | 
|  | /// <summary> | 
|  | /// Represents an Excel Chartsheet and provides access to its properties and methods | 
|  | /// </summary> | 
|  | public class ExcelChartsheet : ExcelWorksheet | 
|  | { | 
|  | //ExcelDrawings draws; | 
|  | public ExcelChartsheet(XmlNamespaceManager ns, ExcelPackage pck, string relID, Uri uriWorksheet, string sheetName, int sheetID, int positionID, eWorkSheetHidden hidden, eChartType chartType) : | 
|  | base(ns, pck, relID, uriWorksheet, sheetName, sheetID, positionID, hidden) | 
|  | { | 
|  | this.Drawings.AddChart("Chart 1", chartType); | 
|  | } | 
|  | public ExcelChartsheet(XmlNamespaceManager ns, ExcelPackage pck, string relID, Uri uriWorksheet, string sheetName, int sheetID, int positionID, eWorkSheetHidden hidden) : | 
|  | base(ns, pck, relID, uriWorksheet, sheetName, sheetID, positionID, hidden) | 
|  | { | 
|  | } | 
|  | public ExcelChart Chart | 
|  | { | 
|  | get | 
|  | { | 
|  | return (ExcelChart)Drawings[0]; | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Represents an Excel worksheet and provides access to its properties and methods | 
|  | /// </summary> | 
|  | public class ExcelWorksheet : XmlHelper, IEqualityComparer<ExcelWorksheet>, IDisposable | 
|  | { | 
|  | internal class Formulas | 
|  | { | 
|  | public Formulas(ISourceCodeTokenizer tokenizer) | 
|  | { | 
|  | _tokenizer = tokenizer; | 
|  | } | 
|  |  | 
|  | public static string RemoveDummyFunction(string formula) | 
|  | { | 
|  | const string DummyFunctionPrefix = "IFERROR(__xludf.DUMMYFUNCTION(\""; | 
|  | const string DummyFunctionSuffix = "\"),\""; | 
|  |  | 
|  | if (string.IsNullOrEmpty(formula)) | 
|  | return formula; | 
|  |  | 
|  | // Look for Prefix | 
|  | if (!formula.StartsWith(DummyFunctionPrefix)) | 
|  | return formula; | 
|  |  | 
|  | // Look for Suffix | 
|  | int index = formula.LastIndexOf(DummyFunctionSuffix); | 
|  | if (index < 0) | 
|  | return formula; | 
|  |  | 
|  | // Trim Suffix | 
|  | formula = formula.Substring(0, index); | 
|  |  | 
|  | // Trim Prefix | 
|  | formula = formula.Replace(DummyFunctionPrefix, ""); | 
|  |  | 
|  | // Replace doubled quotes with single quote | 
|  | formula = formula.Replace("\"\"", "\""); | 
|  |  | 
|  | // Return formula | 
|  | return formula; | 
|  | } | 
|  |  | 
|  | private ISourceCodeTokenizer _tokenizer; | 
|  | internal int Index { get; set; } | 
|  | internal string Address { get; set; } | 
|  | internal bool IsArray { get; set; } | 
|  | public string Formula { get; set; } | 
|  | public int StartRow { get; set; } | 
|  | public int StartCol { get; set; } | 
|  |  | 
|  | private IEnumerable<Token> Tokens {get; set;} | 
|  |  | 
|  |  | 
|  | internal string GetFormula(int row, int column, string worksheet) | 
|  | { | 
|  | if ((StartRow == row && StartCol == column) || IsArray) | 
|  | { | 
|  | return RemoveDummyFunction(Formula); | 
|  | } | 
|  |  | 
|  | if (Tokens == null) | 
|  | { | 
|  | Tokens = _tokenizer.Tokenize(RemoveDummyFunction(Formula), worksheet); | 
|  | } | 
|  |  | 
|  | string f = ""; | 
|  | foreach (var token in Tokens) | 
|  | { | 
|  | if (token.TokenType == TokenType.ExcelAddress) | 
|  | { | 
|  | var a = new ExcelFormulaAddress(token.Value); | 
|  | f += a.GetOffset(row - StartRow, column - StartCol); | 
|  | } | 
|  | else | 
|  | { | 
|  | f += token.Value; | 
|  | } | 
|  | } | 
|  | return f; | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Collection containing merged cell addresses | 
|  | /// </summary> | 
|  | public class MergeCellsCollection : IEnumerable<string> | 
|  | { | 
|  | internal MergeCellsCollection() | 
|  | { | 
|  |  | 
|  | } | 
|  | internal CellStore<int> _cells = new CellStore<int>(); | 
|  | List<string> _list = new List<string>(); | 
|  | internal List<string> List { get {return _list;} } | 
|  | public string this[int row, int column] | 
|  | { | 
|  | get | 
|  | { | 
|  | int ix=-1; | 
|  | if (_cells.Exists(row, column, ref ix) && ix >= 0 && ix < List.Count)  //Fixes issue 15075 | 
|  | { | 
|  | return List[ix]; | 
|  | } | 
|  | else | 
|  | { | 
|  | return null; | 
|  | } | 
|  | } | 
|  | } | 
|  | public string this[int index] | 
|  | { | 
|  | get | 
|  | { | 
|  | return _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); | 
|  | } | 
|  | } | 
|  |  | 
|  | private bool Validate(ExcelAddressBase address) | 
|  | { | 
|  | int ix=0; | 
|  | if(_cells.Exists(address._fromRow, address._fromCol, ref ix)) | 
|  | { | 
|  | if (ix>=0 && ix < _list.Count && _list[ix]!=null && address.Address == _list[ix]) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | var cse = new CellsStoreEnumerator<int>(_cells, address._fromRow, address._fromCol, address._toRow, address._toCol); | 
|  | //cells | 
|  | while(cse.Next()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | //Entire column | 
|  | cse = new CellsStoreEnumerator<int>(_cells, 0, address._fromCol, 0, address._toCol); | 
|  | while (cse.Next()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | //Entire row | 
|  | cse = new CellsStoreEnumerator<int>(_cells, address._fromRow, 0, address._toRow, 0); | 
|  | while (cse.Next()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | internal void SetIndex(ExcelAddressBase address, int ix) | 
|  | { | 
|  | if (address._fromRow == 1 && address._toRow == ExcelPackage.MaxRows) //Entire row | 
|  | { | 
|  | for (int col = address._fromCol; col <= address._toCol; col++) | 
|  | { | 
|  | _cells.SetValue(0, col, ix); | 
|  | } | 
|  | } | 
|  | else if (address._fromCol == 1 && address._toCol == ExcelPackage.MaxColumns) //Entire row | 
|  | { | 
|  | for (int row = address._fromRow; row <= address._toRow; row++) | 
|  | { | 
|  | _cells.SetValue(row, 0, ix); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | for (int col = address._fromCol; col <= address._toCol; col++) | 
|  | { | 
|  | for (int row = address._fromRow; row <= address._toRow; row++) | 
|  | { | 
|  | _cells.SetValue(row, col, ix); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | public int Count | 
|  | { | 
|  | get | 
|  | { | 
|  | return _list.Count; | 
|  | } | 
|  | } | 
|  | internal void Remove(string Item) | 
|  | { | 
|  | _list.Remove(Item); | 
|  | } | 
|  | #region IEnumerable<string> Members | 
|  |  | 
|  | public IEnumerator<string> GetEnumerator() | 
|  | { | 
|  | return _list.GetEnumerator(); | 
|  | } | 
|  |  | 
|  | #endregion | 
|  |  | 
|  | #region IEnumerable Members | 
|  |  | 
|  | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | 
|  | { | 
|  | return _list.GetEnumerator(); | 
|  | } | 
|  |  | 
|  | #endregion | 
|  | internal void Clear(ExcelAddressBase Destination) | 
|  | { | 
|  | var cse = new CellsStoreEnumerator<int>(_cells, Destination._fromRow, Destination._fromCol, Destination._toRow, Destination._toCol); | 
|  | var used=new HashSet<int>(); | 
|  | while(cse.Next()) | 
|  | { | 
|  | var v=cse.Value; | 
|  | if (!used.Contains(v) && _list[v]!=null) | 
|  | { | 
|  | var adr=new ExcelAddressBase(_list[v]); | 
|  | if (!(Destination.Collide(adr) == ExcelAddressBase.eAddressCollition.Inside || Destination.Collide(adr)==ExcelAddressBase.eAddressCollition.Equal)) | 
|  | { | 
|  | throw(new InvalidOperationException(string.Format("Can't delete merged cells. A range is partly merged with the deleted range. {0}", adr._address))); | 
|  | } | 
|  | used.Add(v); | 
|  | } | 
|  | } | 
|  |  | 
|  | _cells.Clear(Destination._fromRow, Destination._fromCol, Destination._toRow - Destination._fromRow + 1, Destination._toCol - Destination._fromCol + 1); | 
|  | foreach(var i in used) | 
|  | { | 
|  | _list[i] = null; | 
|  | } | 
|  | } | 
|  | } | 
|  | internal CellStore<object> _values; | 
|  | internal CellStore<string> _types; | 
|  | internal CellStore<int> _styles; | 
|  | internal CellStore<object> _formulas; | 
|  | internal FlagCellStore _flags; | 
|  | internal CellStore<List<Token>> _formulaTokens; | 
|  |  | 
|  | internal CellStore<Uri> _hyperLinks; | 
|  | internal CellStore<ExcelComment> _commentsStore; | 
|  |  | 
|  | internal Dictionary<int, Formulas> _sharedFormulas = new Dictionary<int, Formulas>(); | 
|  | internal int _minCol = ExcelPackage.MaxColumns; | 
|  | internal int _maxCol = 0; | 
|  | #region Worksheet Private Properties | 
|  | internal ExcelPackage _package; | 
|  | private Uri _worksheetUri; | 
|  | private string _name; | 
|  | private int _sheetID; | 
|  | private int _positionID; | 
|  | private string _relationshipID; | 
|  | private XmlDocument _worksheetXml; | 
|  | internal ExcelWorksheetView _sheetView; | 
|  | internal ExcelHeaderFooter _headerFooter; | 
|  | #endregion | 
|  | #region ExcelWorksheet Constructor | 
|  | /// <summary> | 
|  | /// A worksheet | 
|  | /// </summary> | 
|  | /// <param name="ns">Namespacemanager</param> | 
|  | /// <param name="excelPackage">Package</param> | 
|  | /// <param name="relID">Relationship ID</param> | 
|  | /// <param name="uriWorksheet">URI</param> | 
|  | /// <param name="sheetName">Name of the sheet</param> | 
|  | /// <param name="sheetID">Sheet id</param> | 
|  | /// <param name="positionID">Position</param> | 
|  | /// <param name="hide">hide</param> | 
|  | public ExcelWorksheet(XmlNamespaceManager ns, ExcelPackage excelPackage, string relID, | 
|  | Uri uriWorksheet, string sheetName, int sheetID, int positionID, | 
|  | eWorkSheetHidden hide) : | 
|  | base(ns, null) | 
|  | { | 
|  | SchemaNodeOrder = new string[] { "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; | 
|  | _relationshipID = relID; | 
|  | _worksheetUri = uriWorksheet; | 
|  | _name = sheetName; | 
|  | _sheetID = sheetID; | 
|  | _positionID = positionID; | 
|  | Hidden = hide; | 
|  |  | 
|  | /**** Cellstore ****/ | 
|  | _values=new CellStore<object>(); | 
|  | _types = new CellStore<string>(); | 
|  | _styles = new CellStore<int>(); | 
|  | _formulas = new CellStore<object>(); | 
|  | _flags = new FlagCellStore(); | 
|  | _commentsStore = new CellStore<ExcelComment>(); | 
|  | _hyperLinks = new CellStore<Uri>(); | 
|  |  | 
|  | _names = new ExcelNamedRangeCollection(Workbook,this); | 
|  |  | 
|  | CreateXml(); | 
|  | TopNode = _worksheetXml.DocumentElement; | 
|  | } | 
|  |  | 
|  | #endregion | 
|  | /// <summary> | 
|  | /// The Uri to the worksheet within the package | 
|  | /// </summary> | 
|  | internal Uri WorksheetUri { get { return (_worksheetUri); } } | 
|  | /// <summary> | 
|  | /// The Zip.ZipPackagePart for the worksheet within the package | 
|  | /// </summary> | 
|  | internal Packaging.ZipPackagePart Part { get { return (_package.Package.GetPart(WorksheetUri)); } } | 
|  | /// <summary> | 
|  | /// The ID for the worksheet's relationship with the workbook in the package | 
|  | /// </summary> | 
|  | internal string RelationshipID { get { return (_relationshipID); } } | 
|  | /// <summary> | 
|  | /// The unique identifier for the worksheet. | 
|  | /// </summary> | 
|  | internal int SheetID { get { return (_sheetID); } } | 
|  | /// <summary> | 
|  | /// The position of the worksheet. | 
|  | /// </summary> | 
|  | internal int PositionID { get { return (_positionID); } set { _positionID = value; } } | 
|  | #region Worksheet Public Properties | 
|  | /// <summary> | 
|  | /// The index in the worksheets collection | 
|  | /// </summary> | 
|  | public int Index { get { return (_positionID); } } | 
|  | /// <summary> | 
|  | /// Address for autofilter | 
|  | /// <seealso cref="ExcelRangeBase.AutoFilter" /> | 
|  | /// </summary> | 
|  | public ExcelAddressBase AutoFilterAddress | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | string address = GetXmlNodeString("d:autoFilter/@ref"); | 
|  | if (address == "") | 
|  | { | 
|  | return null; | 
|  | } | 
|  | else | 
|  | { | 
|  | return new ExcelAddressBase(address); | 
|  | } | 
|  | } | 
|  | internal set | 
|  | { | 
|  | CheckSheetType(); | 
|  | SetXmlNodeString("d:autoFilter/@ref", value.Address); | 
|  | } | 
|  | } | 
|  |  | 
|  | internal void CheckSheetType() | 
|  | { | 
|  | if (this is ExcelChartsheet) | 
|  | { | 
|  | throw (new NotSupportedException("This property or method is not supported for a Chartsheet")); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns a ExcelWorksheetView object that allows you to set the view state properties of the worksheet | 
|  | /// </summary> | 
|  | public ExcelWorksheetView View | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_sheetView == null) | 
|  | { | 
|  | XmlNode node = TopNode.SelectSingleNode("d:sheetViews/d:sheetView", NameSpaceManager); | 
|  | if (node == null) | 
|  | { | 
|  | CreateNode("d:sheetViews/d:sheetView"); //this one shouls always exist. but check anyway | 
|  | node = TopNode.SelectSingleNode("d:sheetViews/d:sheetView", NameSpaceManager); | 
|  | } | 
|  | _sheetView = new ExcelWorksheetView(NameSpaceManager, node, this); | 
|  | } | 
|  | return (_sheetView); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// The worksheet's display name as it appears on the tab | 
|  | /// </summary> | 
|  | public string Name | 
|  | { | 
|  | get { return (_name); } | 
|  | set | 
|  | { | 
|  | if (value == _name) return; | 
|  | value=_package.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")); | 
|  | } | 
|  | } | 
|  | _package.Workbook.SetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@name", _sheetID), value); | 
|  | ChangeNames(value); | 
|  |  | 
|  | _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; | 
|  | /// <summary> | 
|  | /// Provides access to named ranges | 
|  | /// </summary> | 
|  | public ExcelNamedRangeCollection Names | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return _names; | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Indicates if the worksheet is hidden in the workbook | 
|  | /// </summary> | 
|  | public eWorkSheetHidden Hidden | 
|  | { | 
|  | get | 
|  | { | 
|  | string state=_package.Workbook.GetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID)); | 
|  | if (state == "hidden") | 
|  | { | 
|  | return eWorkSheetHidden.Hidden; | 
|  | } | 
|  | else if (state == "veryHidden") | 
|  | { | 
|  | return eWorkSheetHidden.VeryHidden; | 
|  | } | 
|  | return eWorkSheetHidden.Visible; | 
|  | } | 
|  | set | 
|  | { | 
|  | if (value == eWorkSheetHidden.Visible) | 
|  | { | 
|  | _package.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); | 
|  | _package.Workbook.SetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID),v ); | 
|  | } | 
|  | } | 
|  | } | 
|  | double _defaultRowHeight = double.NaN; | 
|  | /// <summary> | 
|  | /// Get/set the default height of all rows in the worksheet | 
|  | /// </summary> | 
|  | public double DefaultRowHeight | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (double.IsNaN(_defaultRowHeight)) | 
|  | { | 
|  | _defaultRowHeight = GetXmlNodeDouble("d:sheetFormatPr/@defaultRowHeight"); | 
|  | if(double.IsNaN(_defaultRowHeight)) | 
|  | { | 
|  | _defaultRowHeight = 15; // Excel default height | 
|  | } | 
|  | } | 
|  | return _defaultRowHeight; | 
|  | } | 
|  | set | 
|  | { | 
|  | CheckSheetType(); | 
|  | _defaultRowHeight = value; | 
|  | SetXmlNodeString("d:sheetFormatPr/@defaultRowHeight", value.ToString(CultureInfo.InvariantCulture)); | 
|  | SetXmlNodeBool("d:sheetFormatPr/@customHeight", value != 15); | 
|  |  | 
|  | if (double.IsNaN(GetXmlNodeDouble("d:sheetFormatPr/@defaultColWidth"))) | 
|  | { | 
|  | DefaultColWidth = 9.140625; | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Get/set the default width of all rows in the worksheet | 
|  | /// </summary> | 
|  | public double DefaultColWidth | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | double ret = GetXmlNodeDouble("d:sheetFormatPr/@defaultColWidth"); | 
|  | if (double.IsNaN(ret)) | 
|  | { | 
|  | ret = 9.140625; // Excel's default width | 
|  | } | 
|  | return ret; | 
|  | } | 
|  | set | 
|  | { | 
|  | CheckSheetType(); | 
|  | SetXmlNodeString("d:sheetFormatPr/@defaultColWidth", value.ToString(CultureInfo.InvariantCulture)); | 
|  |  | 
|  | if (double.IsNaN(GetXmlNodeDouble("d:sheetFormatPr/@defaultRowHeight"))) | 
|  | { | 
|  | DefaultRowHeight = 15; | 
|  | } | 
|  | } | 
|  | } | 
|  | /** <outlinePr applyStyles="1" summaryBelow="0" summaryRight="0" /> **/ | 
|  | const string outLineSummaryBelowPath = "d:sheetPr/d:outlinePr/@summaryBelow"; | 
|  | /// <summary> | 
|  | /// Summary rows below details | 
|  | /// </summary> | 
|  | public bool OutLineSummaryBelow | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return GetXmlNodeBool(outLineSummaryBelowPath); | 
|  | } | 
|  | set | 
|  | { | 
|  | CheckSheetType(); | 
|  | SetXmlNodeString(outLineSummaryBelowPath, value ? "1" : "0"); | 
|  | } | 
|  | } | 
|  | const string outLineSummaryRightPath = "d:sheetPr/d:outlinePr/@summaryRight"; | 
|  | /// <summary> | 
|  | /// Summary rows to right of details | 
|  | /// </summary> | 
|  | public bool OutLineSummaryRight | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return GetXmlNodeBool(outLineSummaryRightPath); | 
|  | } | 
|  | set | 
|  | { | 
|  | CheckSheetType(); | 
|  | SetXmlNodeString(outLineSummaryRightPath, value ? "1" : "0"); | 
|  | } | 
|  | } | 
|  | const string outLineApplyStylePath = "d:sheetPr/d:outlinePr/@applyStyles"; | 
|  | /// <summary> | 
|  | /// Automatic styles | 
|  | /// </summary> | 
|  | public bool OutLineApplyStyle | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return GetXmlNodeBool(outLineApplyStylePath); | 
|  | } | 
|  | set | 
|  | { | 
|  | CheckSheetType(); | 
|  | SetXmlNodeString(outLineApplyStylePath, value ? "1" : "0"); | 
|  | } | 
|  | } | 
|  | const string tabColorPath = "d:sheetPr/d:tabColor/@rgb"; | 
|  | /// <summary> | 
|  | /// Color of the sheet tab | 
|  | /// </summary> | 
|  | public Color TabColor | 
|  | { | 
|  | get | 
|  | { | 
|  | string col = GetXmlNodeString(tabColorPath); | 
|  | if (col == "") | 
|  | { | 
|  | return Color.Empty; | 
|  | } | 
|  | else | 
|  | { | 
|  | return Color.FromArgb(int.Parse(col, System.Globalization.NumberStyles.AllowHexSpecifier)); | 
|  | } | 
|  | } | 
|  | set | 
|  | { | 
|  | SetXmlNodeString(tabColorPath, value.ToArgb().ToString("X")); | 
|  | } | 
|  | } | 
|  | const string codeModuleNamePath = "d:sheetPr/@codeName"; | 
|  | internal string CodeModuleName | 
|  | { | 
|  | get | 
|  | { | 
|  | return GetXmlNodeString(codeModuleNamePath); | 
|  | } | 
|  | set | 
|  | { | 
|  | SetXmlNodeString(codeModuleNamePath, value); | 
|  | } | 
|  | } | 
|  | internal void CodeNameChange(string value) | 
|  | { | 
|  | CodeModuleName = value; | 
|  | } | 
|  | public VBA.ExcelVBAModule CodeModule | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_package.Workbook.VbaProject != null) | 
|  | { | 
|  | return _package.Workbook.VbaProject.Modules[CodeModuleName]; | 
|  | } | 
|  | else | 
|  | { | 
|  | return null; | 
|  | } | 
|  | } | 
|  | } | 
|  | #region WorksheetXml | 
|  | /// <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 | 
|  | { | 
|  | get | 
|  | { | 
|  | return (_worksheetXml); | 
|  | } | 
|  | } | 
|  | internal ExcelVmlDrawingCommentCollection _vmlDrawings = null; | 
|  | /// <summary> | 
|  | /// Vml drawings. underlaying object for comments | 
|  | /// </summary> | 
|  | internal ExcelVmlDrawingCommentCollection VmlDrawingsComments | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_vmlDrawings == null) | 
|  | { | 
|  | CreateVmlCollection(); | 
|  | } | 
|  | return _vmlDrawings; | 
|  | } | 
|  | } | 
|  | internal ExcelCommentCollection _comments = null; | 
|  | /// <summary> | 
|  | /// Collection of comments | 
|  | /// </summary> | 
|  | public ExcelCommentCollection Comments | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (_comments == null) | 
|  | { | 
|  | CreateVmlCollection(); | 
|  | _comments = new ExcelCommentCollection(_package, this, NameSpaceManager); | 
|  | } | 
|  | return _comments; | 
|  | } | 
|  | } | 
|  | private void CreateVmlCollection() | 
|  | { | 
|  | var vmlNode = _worksheetXml.DocumentElement.SelectSingleNode("d:legacyDrawing/@r:id", NameSpaceManager); | 
|  | if (vmlNode == null) | 
|  | { | 
|  | _vmlDrawings = new ExcelVmlDrawingCommentCollection(_package, this, null); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (Part.RelationshipExists(vmlNode.Value)) | 
|  | { | 
|  | var rel = Part.GetRelationship(vmlNode.Value); | 
|  | var vmlUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri); | 
|  |  | 
|  | _vmlDrawings = new ExcelVmlDrawingCommentCollection(_package, this, vmlUri); | 
|  | _vmlDrawings.RelId = rel.Id; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void CreateXml() | 
|  | { | 
|  | _worksheetXml = new XmlDocument(); | 
|  | _worksheetXml.PreserveWhitespace = ExcelPackage.preserveWhitespace; | 
|  | Packaging.ZipPackagePart packPart = _package.Package.GetPart(WorksheetUri); | 
|  | string xml = ""; | 
|  |  | 
|  | // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from a xmlstream to optimize speed... | 
|  | bool doAdjust = _package.DoAdjustDrawings; | 
|  | _package.DoAdjustDrawings = false; | 
|  | 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); | 
|  | Encoding encoding; | 
|  | xml = GetWorkSheetXml(stream, start, end, out encoding); | 
|  |  | 
|  | //first char is invalid sometimes?? | 
|  | if (xml[0] != '<') | 
|  | LoadXmlSafe(_worksheetXml, xml.Substring(1, xml.Length - 1), encoding); | 
|  | else | 
|  | LoadXmlSafe(_worksheetXml, xml, encoding); | 
|  |  | 
|  | _package.DoAdjustDrawings = doAdjust; | 
|  | ClearNodes(); | 
|  | } | 
|  | /// <summary> | 
|  | /// Get the lenth of the attributes | 
|  | /// Conditional formatting attributes can be extremly long som get length of the attributes to finetune position. | 
|  | /// </summary> | 
|  | /// <param name="xr"></param> | 
|  | /// <returns></returns> | 
|  | private int GetAttributeLength(XmlTextReader xr) | 
|  | { | 
|  | if (xr.NodeType != XmlNodeType.Element) return 0; | 
|  | var length = 0; | 
|  |  | 
|  | for (int i = 0; i < xr.AttributeCount; i++) | 
|  | { | 
|  | var a=xr.GetAttribute(i); | 
|  | length += string.IsNullOrEmpty(a) ? 0 : a.Length; | 
|  | } | 
|  | return length; | 
|  | } | 
|  | private void LoadRowPageBreakes(XmlTextReader xr) | 
|  | { | 
|  | if(!ReadUntil(xr, "rowBreaks","colBreaks")) return; | 
|  | while (xr.Read()) | 
|  | { | 
|  | if (xr.LocalName == "brk") | 
|  | { | 
|  | if (xr.NodeType == XmlNodeType.Element) | 
|  | { | 
|  | int id; | 
|  | if (int.TryParse(xr.GetAttribute("id"), out id)) | 
|  | { | 
|  | Row(id).PageBreak = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | private void LoadColPageBreakes(XmlTextReader xr) | 
|  | { | 
|  | if (!ReadUntil(xr, "colBreaks")) return; | 
|  | while (xr.Read()) | 
|  | { | 
|  | if (xr.LocalName == "brk") | 
|  | { | 
|  | if (xr.NodeType == XmlNodeType.Element) | 
|  | { | 
|  | int id; | 
|  | if (int.TryParse(xr.GetAttribute("id"), out id)) | 
|  | { | 
|  | Column(id).PageBreak = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void ClearNodes() | 
|  | { | 
|  | 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: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:colBreaks", NameSpaceManager) != null) | 
|  | { | 
|  | _worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll(); | 
|  | } | 
|  | } | 
|  | const int BLOCKSIZE=8192; | 
|  | private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding encoding) | 
|  | { | 
|  | StreamReader sr = new StreamReader(stream); | 
|  | int length = 0; | 
|  | char[] block; | 
|  | int pos; | 
|  | StringBuilder sb = new StringBuilder(); | 
|  | Match startmMatch, endMatch; | 
|  | do | 
|  | { | 
|  | int size = stream.Length < BLOCKSIZE ? (int)stream.Length : BLOCKSIZE; | 
|  | block = new char[size]; | 
|  | pos = sr.ReadBlock(block, 0, size); | 
|  | sb.Append(block,0,pos); | 
|  | length += size; | 
|  | } | 
|  | while (length < start + 20 && length < end); | 
|  | startmMatch = Regex.Match(sb.ToString(), string.Format("(<[^>]*{0}[^>]*>)", "sheetData")); | 
|  | if (!startmMatch.Success) //Not found | 
|  | { | 
|  | encoding = sr.CurrentEncoding; | 
|  | return sb.ToString(); | 
|  | } | 
|  | else | 
|  | { | 
|  | string s = sb.ToString(); | 
|  | string xml = s.Substring(0, startmMatch.Index); | 
|  | if(startmMatch.Value.EndsWith("/>")) | 
|  | { | 
|  | xml += s.Substring(startmMatch.Index, s.Length - startmMatch.Index); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (sr.Peek() != -1) | 
|  | { | 
|  | /**** Fixes issue 14788. Fix by Philip Garrett ****/ | 
|  | long endSeekStart = end; | 
|  |  | 
|  | while (endSeekStart >= 0) | 
|  | { | 
|  | endSeekStart = Math.Max(endSeekStart - BLOCKSIZE, 0); | 
|  | int size = (int)(end - endSeekStart); | 
|  | stream.Seek(endSeekStart, SeekOrigin.Begin); | 
|  | block = new char[size]; | 
|  | sr = new StreamReader(stream); | 
|  | pos = sr.ReadBlock(block, 0, size); | 
|  | sb = new StringBuilder(); | 
|  | sb.Append(block, 0, pos); | 
|  | s = sb.ToString(); | 
|  | endMatch = Regex.Match(s, string.Format("(</[^>]*{0}[^>]*>)", "sheetData")); | 
|  | if (endMatch.Success) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | endMatch = Regex.Match(s, string.Format("(</[^>]*{0}[^>]*>)", "sheetData")); | 
|  | xml += "<sheetData/>" + s.Substring(endMatch.Index + endMatch.Length, s.Length - (endMatch.Index + endMatch.Length)); | 
|  | } | 
|  | if (sr.Peek() > -1) | 
|  | { | 
|  | xml += sr.ReadToEnd(); | 
|  | } | 
|  |  | 
|  | encoding = sr.CurrentEncoding; | 
|  | return xml; | 
|  | } | 
|  | } | 
|  | private void GetBlockPos(string xml, string tag, ref int start, ref int end) | 
|  | { | 
|  | Match startmMatch, endMatch; | 
|  | startmMatch = Regex.Match(xml.Substring(start), string.Format("(<[^>]*{0}[^>]*>)", tag)); //"<[a-zA-Z:]*" + tag + "[?]*>"); | 
|  |  | 
|  | if (!startmMatch.Success) //Not found | 
|  | { | 
|  | start = -1; | 
|  | end = -1; | 
|  | return; | 
|  | } | 
|  | var startPos=startmMatch.Index+start; | 
|  | if(startmMatch.Value.Substring(startmMatch.Value.Length-2,1)=="/") | 
|  | { | 
|  | end = startPos + startmMatch.Length; | 
|  | } | 
|  | else | 
|  | { | 
|  | endMatch = Regex.Match(xml.Substring(start), string.Format("(</[^>]*{0}[^>]*>)", tag)); | 
|  | if (endMatch.Success) | 
|  | { | 
|  | end = endMatch.Index + endMatch.Length + start; | 
|  | } | 
|  | } | 
|  | start = startPos; | 
|  | } | 
|  | private bool ReadUntil(XmlTextReader xr,params string[] tagName) | 
|  | { | 
|  | if (xr.EOF) return false; | 
|  | while (!Array.Exists(tagName, tag => xr.LocalName.EndsWith(tag))) | 
|  | { | 
|  | xr.Read(); | 
|  | if (xr.EOF) return false; | 
|  | } | 
|  | return (xr.LocalName.EndsWith(tagName[0])); | 
|  | } | 
|  | private void LoadColumns (XmlTextReader xr)//(string xml) | 
|  | { | 
|  | var colList = new List<IRangeID>(); | 
|  | if (ReadUntil(xr, "cols", "sheetData")) | 
|  | { | 
|  | //if (xml != "") | 
|  | //{ | 
|  | //var xr=new XmlTextReader(new StringReader(xml)); | 
|  | while(xr.Read()) | 
|  | { | 
|  | if (xr.NodeType == XmlNodeType.Whitespace) continue; | 
|  | if (xr.LocalName != "col") break; | 
|  | if (xr.NodeType == XmlNodeType.Element) | 
|  | { | 
|  | int min = int.Parse(xr.GetAttribute("min")); | 
|  |  | 
|  | ExcelColumn col = new ExcelColumn(this, min); | 
|  |  | 
|  | col.ColumnMax = int.Parse(xr.GetAttribute("max")); | 
|  | col.Width = xr.GetAttribute("width") == null ? 0 : double.Parse(xr.GetAttribute("width"), CultureInfo.InvariantCulture); | 
|  | col.BestFit = xr.GetAttribute("bestFit") != null && xr.GetAttribute("bestFit") == "1" ? true : false; | 
|  | col.Collapsed = xr.GetAttribute("collapsed") != null && xr.GetAttribute("collapsed") == "1" ? true : false; | 
|  | col.Phonetic = xr.GetAttribute("phonetic") != null && xr.GetAttribute("phonetic") == "1" ? true : false; | 
|  | col.OutlineLevel = (short)(xr.GetAttribute("outlineLevel") == null ? 0 : int.Parse(xr.GetAttribute("outlineLevel"), CultureInfo.InvariantCulture)); | 
|  | col.Hidden = xr.GetAttribute("hidden") != null && xr.GetAttribute("hidden") == "1" ? true : false; | 
|  | _values.SetValue(0, min, col); | 
|  |  | 
|  | int style; | 
|  | if (!(xr.GetAttribute("style") == null || !int.TryParse(xr.GetAttribute("style"), out style))) | 
|  | { | 
|  | _styles.SetValue(0, min, style); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Read until the node is found. If not found the xmlreader is reseted. | 
|  | /// </summary> | 
|  | /// <param name="xr">The reader</param> | 
|  | /// <param name="nodeText">Text to search for</param> | 
|  | /// <param name="altNode">Alternative text to search for</param> | 
|  | /// <returns></returns> | 
|  | private static bool ReadXmlReaderUntil(XmlTextReader xr, string nodeText, string altNode) | 
|  | { | 
|  | do | 
|  | { | 
|  | if (xr.LocalName == nodeText || xr.LocalName == altNode) return true; | 
|  | } | 
|  | while(xr.Read()); | 
|  | xr.Close(); | 
|  | return false; | 
|  | } | 
|  | /// <summary> | 
|  | /// Load Hyperlinks | 
|  | /// </summary> | 
|  | /// <param name="xr">The reader</param> | 
|  | private void LoadHyperLinks(XmlTextReader xr) | 
|  | { | 
|  | if (!ReadUntil(xr, "hyperlinks", "rowBreaks", "colBreaks")) return; | 
|  | while (xr.Read()) | 
|  | { | 
|  | if (xr.LocalName == "hyperlink") | 
|  | { | 
|  | int fromRow, fromCol, toRow, toCol; | 
|  | ExcelCellBase.GetRowColFromAddress(xr.GetAttribute("ref"), out fromRow, out fromCol, out toRow, out toCol); | 
|  | ExcelHyperLink hl = null; | 
|  | if (xr.GetAttribute("id", ExcelPackage.schemaRelationships) != null) | 
|  | { | 
|  | var rId = xr.GetAttribute("id", ExcelPackage.schemaRelationships); | 
|  | var uri = Part.GetRelationship(rId).TargetUri; | 
|  |  | 
|  | // Get Location, if any.  EPPlus Bug 15517 | 
|  | var location = xr.GetAttribute("location"); | 
|  | location = (string.IsNullOrEmpty(location)) ? "" : "#" + location; | 
|  |  | 
|  | if (uri.IsAbsoluteUri) | 
|  | { | 
|  | try | 
|  | { | 
|  | hl = new ExcelHyperLink(uri.AbsoluteUri + location); | 
|  | } | 
|  | catch | 
|  | { | 
|  | hl = new ExcelHyperLink(uri.OriginalString + location, UriKind.Absolute); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | hl = new ExcelHyperLink(uri.OriginalString + location, UriKind.Relative); | 
|  | } | 
|  |  | 
|  | hl.RId = rId; | 
|  | Part.DeleteRelationship(rId); //Delete the relationship, it is recreated when we save the package. | 
|  | } | 
|  | else if (xr.GetAttribute("location") != null) | 
|  | { | 
|  | hl = new ExcelHyperLink(xr.GetAttribute("location"), xr.GetAttribute("display")); | 
|  | hl.RowSpann = toRow - fromRow; | 
|  | hl.ColSpann = toCol - fromCol; | 
|  | } | 
|  |  | 
|  | string tt = xr.GetAttribute("tooltip"); | 
|  | if (!string.IsNullOrEmpty(tt)) | 
|  | { | 
|  | hl.ToolTip = tt; | 
|  | } | 
|  | _hyperLinks.SetValue(fromRow, fromCol, hl); | 
|  | } | 
|  | else | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Load cells | 
|  | /// </summary> | 
|  | /// <param name="xr">The reader</param> | 
|  | private void LoadCells(XmlTextReader xr) | 
|  | { | 
|  | //var cellList=new List<IRangeID>(); | 
|  | //var rowList = new List<IRangeID>(); | 
|  | //var formulaList = new List<IRangeID>(); | 
|  | ReadUntil(xr, "sheetData", "mergeCells", "hyperlinks", "rowBreaks", "colBreaks"); | 
|  | ExcelAddressBase address=null; | 
|  | string type=""; | 
|  | int style=0; | 
|  | int row = 0; | 
|  | int col = 0; | 
|  | xr.Read(); | 
|  |  | 
|  | while (!xr.EOF) | 
|  | { | 
|  | while (xr.NodeType == XmlNodeType.EndElement) | 
|  | { | 
|  | xr.Read(); | 
|  | continue; | 
|  | } | 
|  | if (xr.LocalName == "row") | 
|  | { | 
|  | var r = xr.GetAttribute("r"); | 
|  | if (r == null) | 
|  | { | 
|  | row++; | 
|  | } | 
|  | else | 
|  | { | 
|  | row = Convert.ToInt32(r); | 
|  | } | 
|  |  | 
|  | if (DoAddRow(xr)) | 
|  | { | 
|  | _values.SetValue(row, 0, AddRow(xr, row)); | 
|  | if(xr.GetAttribute("s") != null) | 
|  | { | 
|  | _styles.SetValue(row, 0, int.Parse(xr.GetAttribute("s"), CultureInfo.InvariantCulture)); | 
|  | } | 
|  | } | 
|  | xr.Read(); | 
|  | } | 
|  | else if (xr.LocalName == "c") | 
|  | { | 
|  | //if (cell != null) cellList.Add(cell); | 
|  | //cell = new ExcelCell(this, xr.GetAttribute("r")); | 
|  | var r = xr.GetAttribute("r"); | 
|  | if (r == null) | 
|  | { | 
|  | //Handle cells with no reference | 
|  | col++; | 
|  | address = new ExcelAddressBase(row, col, row, col); | 
|  | } | 
|  | else | 
|  | { | 
|  | address = new ExcelAddressBase(r); | 
|  | col = address._fromCol; | 
|  | } | 
|  |  | 
|  |  | 
|  | //Datetype | 
|  | if (xr.GetAttribute("t") != null) | 
|  | { | 
|  | type=xr.GetAttribute("t"); | 
|  | _types.SetValue(address._fromRow, address._fromCol, type); | 
|  | } | 
|  | else | 
|  | { | 
|  | type=""; | 
|  | } | 
|  | //Style | 
|  | if(xr.GetAttribute("s") != null) | 
|  | { | 
|  | style=int.Parse(xr.GetAttribute("s")); | 
|  | _styles.SetValue(address._fromRow, address._fromCol, style); | 
|  | _values.SetValue(address._fromRow, address._fromCol, null); //TODO:Better Performance ?? | 
|  | } | 
|  | else | 
|  | { | 
|  | style = 0; | 
|  | } | 
|  | xr.Read(); | 
|  | } | 
|  | else if (xr.LocalName == "v") | 
|  | { | 
|  | SetValueFromXml(xr, type, style, address._fromRow, address._fromCol); | 
|  |  | 
|  | xr.Read(); | 
|  | } | 
|  | else if (xr.LocalName == "f") | 
|  | { | 
|  | string t = xr.GetAttribute("t"); | 
|  | if (t == null) | 
|  | { | 
|  | _formulas.SetValue(address._fromRow, address._fromCol, xr.ReadElementContentAsString()); | 
|  | _values.SetValue(address._fromRow, address._fromCol, null); | 
|  | //formulaList.Add(cell); | 
|  | } | 
|  | else if (t == "shared") | 
|  | { | 
|  |  | 
|  | string si = xr.GetAttribute("si"); | 
|  | if (si != null) | 
|  | { | 
|  | var sfIndex = int.Parse(si); | 
|  | _formulas.SetValue(address._fromRow, address._fromCol, sfIndex); | 
|  | _values.SetValue(address._fromRow, address._fromCol, null); | 
|  | string fAddress = xr.GetAttribute("ref"); | 
|  | string formula = ConvertUtil.ExcelDecodeString(xr.ReadElementContentAsString()); | 
|  | if (formula != "") | 
|  | { | 
|  | _sharedFormulas.Add(sfIndex, new Formulas(SourceCodeTokenizer.Default) { Index = sfIndex, Formula = formula, Address = fAddress, StartRow = address._fromRow, StartCol = address._fromCol }); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | xr.Read();  //Something is wrong in the sheet, read next | 
|  | } | 
|  | } | 
|  | else if (t == "array") //TODO: Array functions are not support yet. Read the formula for the start cell only. | 
|  | { | 
|  | string aAddress = xr.GetAttribute("ref"); | 
|  | ExcelRange addressRange = new ExcelRange(this, aAddress); | 
|  | string formula = xr.ReadElementContentAsString(); | 
|  | bool isIndexMatchFormula = Regex.IsMatch(formula, @"INDEX\(", RegexOptions.IgnoreCase) && Regex.IsMatch(formula, @"MATCH\(", RegexOptions.IgnoreCase) && !aAddress.Contains(":"); | 
|  | if (isIndexMatchFormula) | 
|  | { | 
|  | addressRange.IsArrayFormula = false; | 
|  | for (int colIndex = addressRange.Start.Column; colIndex <= addressRange.End.Column; colIndex++) | 
|  | { | 
|  | for (int rowIndex = addressRange.Start.Row; rowIndex <= addressRange.End.Row; rowIndex++) | 
|  | { | 
|  | var afIndex = GetMaxShareFunctionIndex(true); | 
|  | _formulas.SetValue(rowIndex, colIndex, afIndex); | 
|  | _values.SetValue(rowIndex, colIndex, null); | 
|  | _sharedFormulas.Add(afIndex, new Formulas(SourceCodeTokenizer.Default) { Index = afIndex, Formula = formula, Address = aAddress, StartRow = address._fromRow, StartCol = address._fromCol, IsArray = false }); | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | addressRange.IsArrayFormula = true; | 
|  | var afIndex = GetMaxShareFunctionIndex(true); | 
|  | for (int colIndex = addressRange.Start.Column; colIndex <= addressRange.End.Column; colIndex++) | 
|  | { | 
|  | for (int rowIndex = addressRange.Start.Row; rowIndex <= addressRange.End.Row; rowIndex++) | 
|  | { | 
|  | _formulas.SetValue(rowIndex, colIndex, afIndex); | 
|  | _values.SetValue(rowIndex, colIndex, null); | 
|  | } | 
|  | } | 
|  | _sharedFormulas.Add(afIndex, new Formulas(SourceCodeTokenizer.Default) { Index = afIndex, Formula = formula, Address = aAddress, StartRow = address._fromRow, StartCol = address._fromCol, IsArray = true }); | 
|  | } | 
|  | } | 
|  | else // ??? some other type | 
|  | { | 
|  | xr.Read();  //Something is wrong in the sheet, read next | 
|  | } | 
|  |  | 
|  | } | 
|  | else if (xr.LocalName == "is")   //Inline string | 
|  | { | 
|  | xr.Read(); | 
|  | if (xr.LocalName == "t") | 
|  | { | 
|  | _values.SetValue(address._fromRow, address._fromCol, ConvertUtil.ExcelDecodeString(xr.ReadElementContentAsString())); | 
|  | //cell._value = xr.ReadInnerXml(); | 
|  | } | 
|  | else | 
|  | { | 
|  | _values.SetValue(address._fromRow, address._fromCol, xr.ReadOuterXml()); | 
|  | _types.SetValue(address._fromRow, address._fromCol, "rt"); | 
|  | _flags.SetFlagValue(address._fromRow, address._fromCol, true, CellFlags.RichText); | 
|  | //cell.IsRichText = true; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | //if (cell != null) cellList.Add(cell); | 
|  |  | 
|  | //_cells = new RangeCollection(cellList); | 
|  | //_rows = new RangeCollection(rowList); | 
|  | //_formulaCells = new RangeCollection(formulaList); | 
|  | } | 
|  |  | 
|  | private bool DoAddRow(XmlTextReader xr) | 
|  | { | 
|  | var c = xr.GetAttribute("r")==null ? 0:1; | 
|  | if (xr.GetAttribute("spans") != null) | 
|  | { | 
|  | c++; | 
|  | } | 
|  | return xr.AttributeCount > c; | 
|  | } | 
|  | /// <summary> | 
|  | /// Load merged cells | 
|  | /// </summary> | 
|  | /// <param name="xr"></param> | 
|  | private void LoadMergeCells(XmlTextReader xr) | 
|  | { | 
|  | if(ReadUntil(xr, "mergeCells", "hyperlinks", "rowBreaks", "colBreaks") && !xr.EOF) | 
|  | { | 
|  | while (xr.Read()) | 
|  | { | 
|  | if (xr.LocalName != "mergeCell") break; | 
|  | 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); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Update merged cells | 
|  | /// </summary> | 
|  | /// <param name="sw">The writer</param> | 
|  | private void UpdateMergedCells(StreamWriter sw) | 
|  | { | 
|  | sw.Write("<mergeCells>"); | 
|  | foreach (string address in _mergedCells) | 
|  | { | 
|  | sw.Write("<mergeCell ref=\"{0}\" />", address); | 
|  | } | 
|  | sw.Write("</mergeCells>"); | 
|  | } | 
|  | /// <summary> | 
|  | /// Reads a row from the XML reader | 
|  | /// </summary> | 
|  | /// <param name="xr">The reader</param> | 
|  | /// <param name="row">The row number</param> | 
|  | /// <returns></returns> | 
|  | private RowInternal AddRow(XmlTextReader xr, int row) | 
|  | { | 
|  | return new RowInternal() | 
|  | { | 
|  | Collapsed=(xr.GetAttribute("collapsed") != null && xr.GetAttribute("collapsed")== "1" ? true : false), | 
|  | OutlineLevel = (xr.GetAttribute("outlineLevel") == null ? (short)0 : short.Parse(xr.GetAttribute("outlineLevel"), CultureInfo.InvariantCulture)), | 
|  | Height = (xr.GetAttribute("ht") == null ? -1 : double.Parse(xr.GetAttribute("ht"), CultureInfo.InvariantCulture)), | 
|  | Hidden = (xr.GetAttribute("hidden") != null && xr.GetAttribute("hidden") == "1" ? true : false), | 
|  | Phonetic = xr.GetAttribute("ph") != null && xr.GetAttribute("ph") == "1" ? true : false, | 
|  | CustomHeight = xr.GetAttribute("customHeight") == null ? false : xr.GetAttribute("customHeight")=="1" | 
|  | }; | 
|  | } | 
|  |  | 
|  | private DateTime DurationFromOADate(double value) | 
|  | { | 
|  | // Convert the Duration value into the corresponding DateTime value. | 
|  | // The Duration value is passed in as a fractional Office Automation double values where the | 
|  | // whole portion is the number of days and the fractional portion of the number is the hours. | 
|  | // For example, the Duration value -1.5 represents a negative duration of one day and 12 hours. | 
|  | // We use this technique rather than calling the 'DateTime.FromOADate()' method, because | 
|  | // this technique works for both positive and negative duration values. unlike 'DateTime.FromOADate()' | 
|  | // which yields the wrong result for negative Duration values. | 
|  | return new DateTime(1899, 12, 30) + TimeSpan.FromDays(value); | 
|  | } | 
|  |  | 
|  | private void SetValueFromXml(XmlTextReader xr, string type, int styleID, int row, int col) | 
|  | { | 
|  | //XmlNode vnode = colNode.SelectSingleNode("d:v", NameSpaceManager); | 
|  | //if (vnode == null) return null; | 
|  | if (type == "s") | 
|  | { | 
|  | int ix = xr.ReadElementContentAsInt(); | 
|  |  | 
|  | // Temporary debugging code to locate intermittent 'Index was out of range' exception. | 
|  | if (ix < 0) | 
|  | { | 
|  | throw new Exception(string.Format("ReadElementContentAsInt returned value '{0}' which is less than zero.", ix)); | 
|  | } | 
|  | if (ix >= _package.Workbook._sharedStringsList.Count) | 
|  | { | 
|  | throw new Exception(string.Format("ReadElementContentAsInt returned index value '{0}' which is greater than _sharedStringsList count of {1}.", ix, _package.Workbook._sharedStringsList.Count)); | 
|  | } | 
|  |  | 
|  | _values.SetValue(row, col, _package.Workbook._sharedStringsList[ix].Text); | 
|  | if (_package.Workbook._sharedStringsList[ix].isRichText) | 
|  | { | 
|  | _flags.SetFlagValue(row, col, true, CellFlags.RichText); | 
|  | } | 
|  | } | 
|  | else if (type == "str") | 
|  | { | 
|  | _values.SetValue(row, col, ConvertUtil.ExcelDecodeString(xr.ReadElementContentAsString())); | 
|  | } | 
|  | else if (type == "b") | 
|  | { | 
|  | _values.SetValue(row, col, (xr.ReadElementContentAsString()!="0")); | 
|  | } | 
|  | else if (type == "e") | 
|  | { | 
|  | _values.SetValue(row, col, GetErrorType(xr.ReadElementContentAsString())); | 
|  | } | 
|  | else | 
|  | { | 
|  | string v = xr.ReadElementContentAsString(); | 
|  | var nf = Workbook.Styles.CellXfs[styleID].NumberFormatId; | 
|  | if ((nf >= 20 && nf <= 21) || (nf >= 45 && nf <= 47))  // Duration | 
|  | { | 
|  | double res; | 
|  | if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) | 
|  | { | 
|  | if (Workbook.Date1904) | 
|  | { | 
|  | res += ExcelWorkbook.date1904Offset; | 
|  | } | 
|  | if (res >= -657435.0 && res < 2958465.9999999) | 
|  | { | 
|  | // Get the Duration value expressed as a DateTime. | 
|  | _values.SetValue(row, col, DurationFromOADate(res)); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Cope with Google Sheets export of cells having a formula. | 
|  | // Rather than exporting the native value, they export the formatted value. | 
|  | _values.SetValue(row, col, v); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // Cope with Google Sheets export of cells having a formula. | 
|  | // Rather than exporting the native value, they export the formatted value. | 
|  | _values.SetValue(row, col, v); | 
|  | } | 
|  | } | 
|  | else if ((nf >= 14 && nf <= 19) || (nf == 22))   // DateTime | 
|  | { | 
|  | double res; | 
|  | if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) | 
|  | { | 
|  | if (Workbook.Date1904) | 
|  | { | 
|  | res += ExcelWorkbook.date1904Offset; | 
|  | } | 
|  | if (res >= -657435.0 && res < 2958465.9999999) | 
|  | { | 
|  | _values.SetValue(row, col, DateTime.FromOADate(res)); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Cope with Google Sheets export of cells having a formula. | 
|  | // Rather than exporting the native value, they export the formatted value. | 
|  | _values.SetValue(row, col, v); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // Cope with Google Sheets export of cells having a formula. | 
|  | // Rather than exporting the native value, they export the formatted value. | 
|  | _values.SetValue(row, col, v); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | double d; | 
|  | if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) | 
|  | { | 
|  | _values.SetValue(row, col, d); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Cope with Google Sheets export of cells having a formula. | 
|  | // Rather than exporting the native value, they export the formatted value. | 
|  | _values.SetValue(row, col, v); | 
|  |  | 
|  | //_values.SetValue(row, col, double.NaN); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private object GetErrorType(string v) | 
|  | { | 
|  | return ExcelErrorValue.Parse(v.ToUpper(CultureInfo.InvariantCulture)); | 
|  | //switch(v.ToUpper()) | 
|  | //{ | 
|  | //    case "#DIV/0!": | 
|  | //        return new ExcelErrorValue.cre(eErrorType.Div0); | 
|  | //    case "#REF!": | 
|  | //        return new ExcelErrorValue(eErrorType.Ref); | 
|  | //    case "#N/A": | 
|  | //        return new ExcelErrorValue(eErrorType.NA); | 
|  | //    case "#NAME?": | 
|  | //        return new ExcelErrorValue(eErrorType.Name); | 
|  | //    case "#NULL!": | 
|  | //        return new ExcelErrorValue(eErrorType.Null); | 
|  | //    case "#NUM!": | 
|  | //        return new ExcelErrorValue(eErrorType.Num); | 
|  | //    default: | 
|  | //        return new ExcelErrorValue(eErrorType.Value); | 
|  | //} | 
|  | } | 
|  | //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); | 
|  | //} | 
|  | #endregion | 
|  | #region HeaderFooter | 
|  | /// <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 ExcelHeaderFooter(NameSpaceManager, headerFooterNode, this); | 
|  | } | 
|  | return (_headerFooter); | 
|  | } | 
|  | } | 
|  | #endregion | 
|  |  | 
|  | #region "PrinterSettings" | 
|  | /// <summary> | 
|  | /// Printer settings | 
|  | /// </summary> | 
|  | public ExcelPrinterSettings PrinterSettings | 
|  | { | 
|  | get | 
|  | { | 
|  | var ps = new ExcelPrinterSettings(NameSpaceManager, TopNode, this); | 
|  | ps.SchemaNodeOrder = SchemaNodeOrder; | 
|  | return ps; | 
|  | } | 
|  | } | 
|  | #endregion | 
|  |  | 
|  | #endregion // END Worksheet Public Properties | 
|  |  | 
|  | #region Worksheet Public Methods | 
|  |  | 
|  | ///// <summary> | 
|  | ///// Provides access to an individual cell within the worksheet. | 
|  | ///// </summary> | 
|  | ///// <param name="row">The row number in the worksheet</param> | 
|  | ///// <param name="col">The column number in the worksheet</param> | 
|  | ///// <returns></returns> | 
|  | //internal ExcelCell Cell(int row, int col) | 
|  | //{ | 
|  | //    return new ExcelCell(_values, row, col); | 
|  | //} | 
|  | /// <summary> | 
|  | /// Provides access to a range of cells | 
|  | /// </summary> | 
|  | public ExcelRange Cells | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return new ExcelRange(this, 1, 1, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Provides access to the selected range of cells | 
|  | /// </summary> | 
|  | public ExcelRange SelectedRange | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return new ExcelRange(this, View.SelectedRange); | 
|  | } | 
|  | } | 
|  | MergeCellsCollection _mergedCells = new MergeCellsCollection(); | 
|  | /// <summary> | 
|  | /// Addresses to merged ranges | 
|  | /// </summary> | 
|  | public MergeCellsCollection MergedCells | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | return _mergedCells; | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Provides access to an individual row within the worksheet so you can set its properties. | 
|  | /// </summary> | 
|  | /// <param name="row">The row number in the worksheet</param> | 
|  | /// <returns></returns> | 
|  | public ExcelRow Row(int row) | 
|  | { | 
|  | //ExcelRow r; | 
|  | //ulong id = ExcelRow.GetRowID(_sheetID, row); | 
|  | //TODO: Fixa. | 
|  | //var v = _values.GetValue(row, 0); | 
|  | //if (v!=null) | 
|  | //{ | 
|  | //    var ri=(RowInternal)v; | 
|  | //    r = new ExcelRow(this, row) | 
|  | //} | 
|  | //else | 
|  | //{ | 
|  | //r = new ExcelRow(this, row); | 
|  | //_values.SetValue(row, 0, r); | 
|  | //_rows.Add(r); | 
|  | //} | 
|  | CheckSheetType(); | 
|  | if (row < 1 || row > ExcelPackage.MaxRows) | 
|  | { | 
|  | throw (new ArgumentException("Row number out of bounds")); | 
|  | } | 
|  | return new ExcelRow(this, row); | 
|  | //return r; | 
|  | } | 
|  | /// <summary> | 
|  | /// Provides access to an individual column within the worksheet so you can set its properties. | 
|  | /// </summary> | 
|  | /// <param name="col">The column number in the worksheet</param> | 
|  | /// <returns></returns> | 
|  | public ExcelColumn Column(int col) | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (col < 1 || col > ExcelPackage.MaxColumns) | 
|  | { | 
|  | throw (new ArgumentException("Column number out of bounds")); | 
|  | } | 
|  | var column = _values.GetValue(0, col) as ExcelColumn; | 
|  | if (column!=null) | 
|  | { | 
|  |  | 
|  | if (column.ColumnMin != column.ColumnMax) | 
|  | { | 
|  | int maxCol = column.ColumnMax; | 
|  | column.ColumnMax = col; | 
|  | ExcelColumn copy = CopyColumn(column, col + 1, maxCol); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | int r=0, c=col; | 
|  | if (_values.PrevCell(ref r, ref c)) | 
|  | { | 
|  | column = _values.GetValue(0, c) as ExcelColumn; | 
|  | int maxCol = column.ColumnMax; | 
|  | if (maxCol >= col) | 
|  | { | 
|  | column.ColumnMax = col-1; | 
|  | if (maxCol > col) | 
|  | { | 
|  | ExcelColumn newC = CopyColumn(column, col + 1, maxCol); | 
|  | } | 
|  | return CopyColumn(column, col, col); | 
|  | } | 
|  | } | 
|  | //foreach (ExcelColumn checkColumn in _columns) | 
|  | //{ | 
|  | //    if (col > checkColumn.ColumnMin && col <= checkColumn.ColumnMax) | 
|  | //    { | 
|  | //        int maxCol = checkColumn.ColumnMax; | 
|  | //        checkColumn.ColumnMax = col - 1; | 
|  | //        if (maxCol > col) | 
|  | //        { | 
|  | //            ExcelColumn newC = CopyColumn(checkColumn, col + 1, maxCol); | 
|  | //        } | 
|  | //        return CopyColumn(checkColumn, col,col); | 
|  | //    } | 
|  | //} | 
|  | column = new ExcelColumn(this, col); | 
|  | _values.SetValue(0, col, column); | 
|  | //_columns.Add(column); | 
|  | } | 
|  | return column; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns the name of the worksheet | 
|  | /// </summary> | 
|  | /// <returns>The name of the worksheet</returns> | 
|  | public override string ToString() | 
|  | { | 
|  | return Name; | 
|  | } | 
|  | internal ExcelColumn CopyColumn(ExcelColumn c, int col, int maxCol) | 
|  | { | 
|  | ExcelColumn newC = new ExcelColumn(this, col); | 
|  | newC.ColumnMax = maxCol < ExcelPackage.MaxColumns ? maxCol : ExcelPackage.MaxColumns; | 
|  | if (c.StyleName != "") | 
|  | newC.StyleName = c.StyleName; | 
|  | else | 
|  | newC.StyleID = c.StyleID; | 
|  |  | 
|  | newC.OutlineLevel = c.OutlineLevel; | 
|  | newC.Phonetic = c.Phonetic; | 
|  | newC.BestFit = c.BestFit; | 
|  | //_columns.Add(newC); | 
|  | _values.SetValue(0, col, newC); | 
|  | newC._width = c._width; | 
|  | newC._hidden = c._hidden; | 
|  | return newC; | 
|  | } | 
|  | /// <summary> | 
|  | /// Make the current worksheet active. | 
|  | /// </summary> | 
|  | public void Select() | 
|  | { | 
|  | View.TabSelected = true; | 
|  | //Select(Address, true); | 
|  | } | 
|  | /// <summary> | 
|  | /// Selects a range in the worksheet. The active cell is the topmost cell. | 
|  | /// Make the current worksheet active. | 
|  | /// </summary> | 
|  | /// <param name="Address">An address range</param> | 
|  | public void Select(string Address) | 
|  | { | 
|  | Select(Address, true); | 
|  | } | 
|  | /// <summary> | 
|  | /// Selects a range in the worksheet. The actice cell is the topmost cell. | 
|  | /// </summary> | 
|  | /// <param name="Address">A range of cells</param> | 
|  | /// <param name="SelectSheet">Make the sheet active</param> | 
|  | public void Select(string Address, bool SelectSheet) | 
|  | { | 
|  | CheckSheetType(); | 
|  | int fromCol, fromRow, toCol, toRow; | 
|  | //Get rows and columns and validate as well | 
|  | ExcelCellBase.GetRowColFromAddress(Address, out fromRow, out fromCol, out toRow, out toCol); | 
|  |  | 
|  | if (SelectSheet) | 
|  | { | 
|  | View.TabSelected = true; | 
|  | } | 
|  | View.SelectedRange = Address; | 
|  | View.ActiveCell = ExcelCellBase.GetAddress(fromRow, fromCol); | 
|  | } | 
|  | /// <summary> | 
|  | /// Selects a range in the worksheet. The active cell is the topmost cell of the first address. | 
|  | /// Make the current worksheet active. | 
|  | /// </summary> | 
|  | /// <param name="Address">An address range</param> | 
|  | public void Select(ExcelAddress Address) | 
|  | { | 
|  | CheckSheetType(); | 
|  | Select(Address, true); | 
|  | } | 
|  | /// <summary> | 
|  | /// Selects a range in the worksheet. The active cell is the topmost cell of the first address. | 
|  | /// </summary> | 
|  | /// <param name="Address">A range of cells</param> | 
|  | /// <param name="SelectSheet">Make the sheet active</param> | 
|  | public void Select(ExcelAddress Address, bool SelectSheet) | 
|  | { | 
|  |  | 
|  | CheckSheetType(); | 
|  | if (SelectSheet) | 
|  | { | 
|  | View.TabSelected = true; | 
|  | } | 
|  | string selAddress = ExcelCellBase.GetAddress(Address.Start.Row, Address.Start.Column) + ":" + ExcelCellBase.GetAddress(Address.End.Row, Address.End.Column); | 
|  | if (Address.Addresses != null) | 
|  | { | 
|  | foreach (var a in Address.Addresses) | 
|  | { | 
|  | selAddress += " " + ExcelCellBase.GetAddress(a.Start.Row, a.Start.Column) + ":" + ExcelCellBase.GetAddress(a.End.Row, a.End.Column); | 
|  | } | 
|  | } | 
|  | View.SelectedRange = selAddress; | 
|  | View.ActiveCell = ExcelCellBase.GetAddress(Address.Start.Row, Address.Start.Column); | 
|  | } | 
|  |  | 
|  | #region InsertRow | 
|  | /// <summary> | 
|  | /// Inserts a new row into the spreadsheet.  Existing rows below the position are | 
|  | /// shifted down.  All formula are updated to take account of the new row. | 
|  | /// </summary> | 
|  | /// <param name="rowFrom">The position of the new row</param> | 
|  | /// <param name="rows">Number of rows to insert</param> | 
|  | public void InsertRow(int rowFrom, int rows) | 
|  | { | 
|  | InsertRow(rowFrom, rows, 0); | 
|  | } | 
|  | /// <summary> | 
|  | /// Inserts a new row into the spreadsheet.  Existing rows below the position are | 
|  | /// shifted down.  All formula are updated to take account of the new row. | 
|  | /// </summary> | 
|  | /// <param name="rowFrom">The position of the new row</param> | 
|  | /// <param name="rows">Number of rows to insert.</param> | 
|  | /// <param name="copyStylesFromRow">Copy Styles from this row. Applied to all inserted rows</param> | 
|  | public void  InsertRow(int rowFrom, int rows, int copyStylesFromRow) | 
|  | { | 
|  | CheckSheetType(); | 
|  | var d = Dimension; | 
|  |  | 
|  | if (rowFrom < 1) | 
|  | { | 
|  | throw (new ArgumentOutOfRangeException("rowFrom can't be lesser that 1")); | 
|  | } | 
|  |  | 
|  | //Check that cells aren't shifted outside the boundries | 
|  | if (d != null && d.End.Row > rowFrom && d.End.Row + rows > ExcelPackage.MaxRows) | 
|  | { | 
|  | throw (new ArgumentOutOfRangeException("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); | 
|  |  | 
|  | 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 = ExcelAddressBase.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, 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); | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Inserts a new column into the spreadsheet.  Existing columns below the position are | 
|  | /// shifted down.  All formula are updated to take account of the new column. | 
|  | /// </summary> | 
|  | /// <param name="columnFrom">The position of the new column</param> | 
|  | /// <param name="columns">Number of columns to insert</param> | 
|  | public void InsertColumn(int columnFrom, int columns) | 
|  | { | 
|  | InsertColumn(columnFrom, columns, 0); | 
|  | } | 
|  | ///<summary> | 
|  | /// Inserts a new column into the spreadsheet.  Existing column to the left are | 
|  | /// shifted.  All formula are updated to take account of the new column. | 
|  | /// </summary> | 
|  | /// <param name="columnFrom">The position of the new column</param> | 
|  | /// <param name="columns">Number of columns to insert.</param> | 
|  | /// <param name="copyStylesFromColumn">Copy Styles from this column. Applied to all inserted columns</param> | 
|  | public void InsertColumn(int columnFrom, int columns, int copyStylesFromColumn) | 
|  | { | 
|  | CheckSheetType(); | 
|  | var d = Dimension; | 
|  |  | 
|  | if (columnFrom < 1) | 
|  | { | 
|  | throw (new ArgumentOutOfRangeException("columnFrom can't be lesser that 1")); | 
|  | } | 
|  | //Check that cells aren't shifted outside the boundries | 
|  | if (d != null && d.End.Column > columnFrom && d.End.Column + columns > ExcelPackage.MaxColumns) | 
|  | { | 
|  | throw (new ArgumentOutOfRangeException("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); | 
|  |  | 
|  | 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 = ExcelAddressBase.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) | 
|  | { | 
|  | lst.Add((ExcelColumn)col); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 = this.Column(columnFrom + c); | 
|  | col.StyleID = this.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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void InsertTableColumns(int columnFrom, int columns, ExcelTable tbl) | 
|  | { | 
|  | var node = tbl.Columns[0].TopNode.ParentNode; | 
|  | var ix = columnFrom - tbl.Address.Start.Column - 1; | 
|  | var insPos = node.ChildNodes[ix]; | 
|  | ix += 2; | 
|  | for (int i = 0; i < columns; i++) | 
|  | { | 
|  | var name = | 
|  | tbl.Columns.GetUniqueName(string.Format("Column{0}", | 
|  | (ix++).ToString(CultureInfo.InvariantCulture))); | 
|  | XmlElement tableColumn = | 
|  | (XmlElement) tbl.TableXml.CreateNode(XmlNodeType.Element, "tableColumn", ExcelPackage.schemaMain); | 
|  | tableColumn.SetAttribute("id", (tbl.Columns.Count + i + 1).ToString(CultureInfo.InvariantCulture)); | 
|  | tableColumn.SetAttribute("name", name); | 
|  | insPos = node.InsertAfter(tableColumn, insPos); | 
|  | } //Create tbl Column | 
|  | tbl._cols = new ExcelTableColumnCollection(tbl); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds a value to the row of merged cells to fix for inserts or deletes | 
|  | /// </summary> | 
|  | /// <param name="row"></param> | 
|  | /// <param name="rows"></param> | 
|  | /// <param name="delete"></param> | 
|  | private void FixMergedCellsRow(int row, int rows, bool delete) | 
|  | { | 
|  | if (delete) | 
|  | { | 
|  | _mergedCells._cells.Delete(row, 0, rows, 0); | 
|  | } | 
|  | else | 
|  | { | 
|  | _mergedCells._cells.Insert(row, 0, rows, 0); | 
|  | } | 
|  |  | 
|  | List<int> removeIndex = new List<int>(); | 
|  | for (int i = 0; i < _mergedCells.Count; i++) | 
|  | { | 
|  | if (!string.IsNullOrEmpty( _mergedCells[i])) | 
|  | { | 
|  | ExcelAddressBase addr = new ExcelAddressBase(_mergedCells[i]), newAddr; | 
|  | if (delete) | 
|  | { | 
|  | newAddr = addr.DeleteRow(row, rows); | 
|  | if (newAddr == null) | 
|  | { | 
|  | removeIndex.Add(i); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | newAddr = addr.AddRow(row, rows); | 
|  | if (newAddr.Address != addr.Address) | 
|  | { | 
|  | //    _mergedCells._cells.Insert(row, 0, rows, 0); | 
|  | _mergedCells.SetIndex(newAddr, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (newAddr.Address != addr.Address) | 
|  | { | 
|  | _mergedCells.List[i] = newAddr._address; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (int i = removeIndex.Count - 1; i >= 0; i--) | 
|  | { | 
|  | _mergedCells.List.RemoveAt(removeIndex[i]); | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Adds a value to the row of merged cells to fix for inserts or deletes | 
|  | /// </summary> | 
|  | /// <param name="column"></param> | 
|  | /// <param name="columns"></param> | 
|  | /// <param name="delete"></param> | 
|  | private void FixMergedCellsColumn(int column, int columns, bool delete) | 
|  | { | 
|  | if (delete) | 
|  | { | 
|  | _mergedCells._cells.Delete(0, column, 0, columns); | 
|  | } | 
|  | else | 
|  | { | 
|  | _mergedCells._cells.Insert(0, column, 0, columns); | 
|  | } | 
|  | List<int> removeIndex = new List<int>(); | 
|  | for (int i = 0; i < _mergedCells.Count; i++) | 
|  | { | 
|  | if (!string.IsNullOrEmpty(_mergedCells[i])) | 
|  | { | 
|  | ExcelAddressBase addr = new ExcelAddressBase(_mergedCells[i]), newAddr; | 
|  | if (delete) | 
|  | { | 
|  | newAddr = addr.DeleteColumn(column, columns); | 
|  | if (newAddr == null) | 
|  | { | 
|  | removeIndex.Add(i); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | newAddr = addr.AddColumn(column, columns); | 
|  | if (newAddr.Address != addr.Address) | 
|  | { | 
|  | _mergedCells.SetIndex(newAddr, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (newAddr.Address != addr.Address) | 
|  | { | 
|  | _mergedCells.List[i] = newAddr._address; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (int i = removeIndex.Count - 1; i >= 0; i--) | 
|  | { | 
|  | _mergedCells.List.RemoveAt(removeIndex[i]); | 
|  | } | 
|  | } | 
|  | private void FixSharedFormulasRows(int position, int rows) | 
|  | { | 
|  | List<Formulas> added = new List<Formulas>(); | 
|  | List<Formulas> deleted = new List<Formulas>(); | 
|  |  | 
|  | foreach (int id in _sharedFormulas.Keys) | 
|  | { | 
|  | var f = _sharedFormulas[id]; | 
|  | int fromCol, fromRow, toCol, toRow; | 
|  |  | 
|  | ExcelCellBase.GetRowColFromAddress(f.Address, out fromRow, out fromCol, out toRow, out toCol); | 
|  | if (position >= fromRow && position+(Math.Abs(rows)) <= toRow) //Insert/delete is whithin the share formula address | 
|  | { | 
|  | if (rows > 0) //Insert | 
|  | { | 
|  | f.Address = ExcelCellBase.GetAddress(fromRow, fromCol) + ":" + ExcelCellBase.GetAddress(position - 1, toCol); | 
|  | if (toRow != fromRow) | 
|  | { | 
|  | Formulas newF = new Formulas(SourceCodeTokenizer.Default); | 
|  | newF.StartCol = f.StartCol; | 
|  | newF.StartRow = position + rows; | 
|  | newF.Address = ExcelCellBase.GetAddress(position + rows, fromCol) + ":" + ExcelCellBase.GetAddress(toRow + rows, toCol); | 
|  | newF.Formula = ExcelCellBase.TranslateFromR1C1(ExcelCellBase.TranslateToR1C1(f.Formula, f.StartRow, f.StartCol), position, f.StartCol); | 
|  | added.Add(newF); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (fromRow - rows < toRow) | 
|  | { | 
|  | f.Address = ExcelCellBase.GetAddress(fromRow, fromCol, toRow+rows, toCol); | 
|  | } | 
|  | else | 
|  | { | 
|  | f.Address = ExcelCellBase.GetAddress(fromRow, fromCol) + ":" + ExcelCellBase.GetAddress(toRow + rows, toCol); | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (position <= toRow) | 
|  | { | 
|  | if (rows > 0) //Insert before shift down | 
|  | { | 
|  | f.StartRow += rows; | 
|  | //f.Formula = ExcelCell.UpdateFormulaReferences(f.Formula, rows, 0, position, 0); //Recalc the cells positions | 
|  | f.Address = ExcelCellBase.GetAddress(fromRow + rows, fromCol) + ":" + ExcelCellBase.GetAddress(toRow + rows, toCol); | 
|  | } | 
|  | else | 
|  | { | 
|  | //Cells[f.Address].SetSharedFormulaID(int.MinValue); | 
|  | if (position <= fromRow && position + Math.Abs(rows) > toRow)  //Delete the formula | 
|  | { | 
|  | deleted.Add(f); | 
|  | } | 
|  | else | 
|  | { | 
|  | toRow = toRow + rows < position - 1 ? position - 1 : toRow + rows; | 
|  | if (position <= fromRow) | 
|  | { | 
|  | fromRow = fromRow + rows < position ? position : fromRow + rows; | 
|  | } | 
|  |  | 
|  | f.Address = ExcelCellBase.GetAddress(fromRow, fromCol, toRow, toCol); | 
|  | Cells[f.Address].SetSharedFormulaID(f.Index); | 
|  | //f.StartRow = fromRow; | 
|  |  | 
|  | //f.Formula = ExcelCell.UpdateFormulaReferences(f.Formula, rows, 0, position, 0); | 
|  |  | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | AddFormulas(added, position, rows); | 
|  |  | 
|  | //Remove formulas | 
|  | foreach (Formulas f in deleted) | 
|  | { | 
|  | _sharedFormulas.Remove(f.Index); | 
|  | } | 
|  |  | 
|  | //Fix Formulas | 
|  | added = new List<Formulas>(); | 
|  | foreach (int id in _sharedFormulas.Keys) | 
|  | { | 
|  | var f = _sharedFormulas[id]; | 
|  | UpdateSharedFormulaRow(ref f, position, rows, ref added); | 
|  | } | 
|  | AddFormulas(added, position, rows); | 
|  | } | 
|  |  | 
|  | private void AddFormulas(List<Formulas> added, int position, int rows) | 
|  | { | 
|  | //Add new formulas | 
|  | foreach (Formulas f in added) | 
|  | { | 
|  | f.Index = GetMaxShareFunctionIndex(false); | 
|  | _sharedFormulas.Add(f.Index, f); | 
|  | Cells[f.Address].SetSharedFormulaID(f.Index); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void UpdateSharedFormulaRow(ref Formulas formula, int startRow, int rows, ref List<Formulas> newFormulas) | 
|  | { | 
|  | int fromRow,fromCol, toRow, toCol; | 
|  | int newFormulasCount = newFormulas.Count; | 
|  | ExcelCellBase.GetRowColFromAddress(formula.Address, out fromRow, out fromCol, out toRow, out toCol); | 
|  | //int refSplits = Regex.Split(formula.Formula, "#REF!").GetUpperBound(0); | 
|  | string formualR1C1; | 
|  | if (rows > 0 || fromRow <= startRow) | 
|  | { | 
|  | formualR1C1 = ExcelRangeBase.TranslateToR1C1(formula.Formula, formula.StartRow, formula.StartCol); | 
|  | formula.Formula = ExcelRangeBase.TranslateFromR1C1(formualR1C1, fromRow, formula.StartCol); | 
|  | } | 
|  | else | 
|  | { | 
|  | formualR1C1 = ExcelRangeBase.TranslateToR1C1(formula.Formula, formula.StartRow-rows, formula.StartCol); | 
|  | formula.Formula = ExcelRangeBase.TranslateFromR1C1(formualR1C1, formula.StartRow, formula.StartCol); | 
|  | } | 
|  | //bool isRef = false; | 
|  | //Formulas restFormula=formula; | 
|  | string prevFormualR1C1 = formualR1C1; | 
|  | for (int row = fromRow; row <= toRow; row++) | 
|  | { | 
|  | for (int col = fromCol; col <= toCol; col++) | 
|  | { | 
|  | string newFormula; | 
|  | string currentFormulaR1C1; | 
|  | if (rows > 0 || row < startRow) | 
|  | { | 
|  | newFormula = ExcelCellBase.UpdateFormulaReferences(ExcelCellBase.TranslateFromR1C1(formualR1C1, row, col), rows, 0, startRow, 0); | 
|  | currentFormulaR1C1 = ExcelRangeBase.TranslateToR1C1(newFormula, row, col); | 
|  | } | 
|  | else | 
|  | { | 
|  | newFormula = ExcelCellBase.UpdateFormulaReferences(ExcelCellBase.TranslateFromR1C1(formualR1C1, row-rows, col), rows, 0, startRow, 0); | 
|  | currentFormulaR1C1 = ExcelRangeBase.TranslateToR1C1(newFormula, row, col); | 
|  | } | 
|  | if (currentFormulaR1C1 != prevFormualR1C1) //newFormula.Contains("#REF!")) | 
|  | { | 
|  | //if (refSplits == 0 || Regex.Split(newFormula, "#REF!").GetUpperBound(0) != refSplits) | 
|  | //{ | 
|  | //isRef = true; | 
|  | if (row == fromRow && col == fromCol) | 
|  | { | 
|  | formula.Formula = newFormula; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (newFormulas.Count == newFormulasCount) | 
|  | { | 
|  | formula.Address = ExcelCellBase.GetAddress(formula.StartRow, formula.StartCol, row - 1, col); | 
|  | } | 
|  | else | 
|  | { | 
|  | newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[newFormulas.Count - 1].StartCol, row - 1, col); | 
|  | } | 
|  | var refFormula = new Formulas(SourceCodeTokenizer.Default); | 
|  | refFormula.Formula = newFormula; | 
|  | refFormula.StartRow = row; | 
|  | refFormula.StartCol = col; | 
|  | newFormulas.Add(refFormula); | 
|  |  | 
|  | //restFormula = null; | 
|  | prevFormualR1C1 = currentFormulaR1C1; | 
|  | } | 
|  | } | 
|  | //    } | 
|  | //    else | 
|  | //    { | 
|  | //        isRef = false; | 
|  | //    } | 
|  | //} | 
|  | //else | 
|  | //{ | 
|  | //    isRef = false; | 
|  | //} | 
|  | //if (restFormula==null) | 
|  | //{ | 
|  | //if (newFormulas.Count == newFormulasCount) | 
|  | //{ | 
|  | //    formula.Address = ExcelCellBase.GetAddress(formula.StartRow, formula.StartCol, row - 1, col); | 
|  | //} | 
|  | //else | 
|  | //{ | 
|  | //                            newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[0].StartCol, row - 1, col); | 
|  | //} | 
|  |  | 
|  | //restFormula = new Formulas(); | 
|  | //restFormula.Formula = newFormula; | 
|  | //restFormula.StartRow = row; | 
|  | //restFormula.StartCol = col; | 
|  | //newFormulas.Add(restFormula); | 
|  | //} | 
|  | } | 
|  | } | 
|  | if (rows < 0 && formula.StartRow > startRow) | 
|  | { | 
|  | if (formula.StartRow + rows < startRow) | 
|  | { | 
|  | formula.StartRow = startRow; | 
|  | } | 
|  | else | 
|  | { | 
|  | formula.StartRow += rows; | 
|  | } | 
|  | } | 
|  | if (newFormulas.Count > newFormulasCount) | 
|  | { | 
|  | newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[newFormulas.Count - 1].StartCol, toRow, toCol); | 
|  | } | 
|  | } | 
|  | #endregion | 
|  |  | 
|  | #region DeleteRow | 
|  | /// <summary> | 
|  | /// Delete the specified row from the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="row">A row to be deleted</param> | 
|  | public void DeleteRow(int row) | 
|  | { | 
|  | DeleteRow(row, 1); | 
|  | } | 
|  | /// <summary> | 
|  | /// Delete the specified row from the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="rowFrom">The start row</param> | 
|  | /// <param name="rows">Number of rows to delete</param> | 
|  | public void DeleteRow(int rowFrom, int rows) | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (rowFrom < 1 || rowFrom + rows > ExcelPackage.MaxRows) | 
|  | { | 
|  | throw(new ArgumentException("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); | 
|  |  | 
|  | AdjustFormulasRow(rowFrom, rows); | 
|  | FixMergedCellsRow(rowFrom, rows, true); | 
|  |  | 
|  | foreach (var tbl in Tables) | 
|  | { | 
|  | tbl.Address = tbl.Address.DeleteRow(rowFrom, rows); | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Delete the specified column from the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="column">The column to be deleted</param> | 
|  | public void DeleteColumn(int column) | 
|  | { | 
|  | DeleteColumn(column,1); | 
|  | } | 
|  | /// <summary> | 
|  | /// Delete the specified column from the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="columnFrom">The start column</param> | 
|  | /// <param name="columns">Number of columns to delete</param> | 
|  | public void DeleteColumn(int columnFrom, int columns) | 
|  | { | 
|  | if (columnFrom < 1 || columnFrom + columns > ExcelPackage.MaxColumns) | 
|  | { | 
|  | throw (new ArgumentException("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; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | _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); | 
|  |  | 
|  | var csec = new CellsStoreEnumerator<object>(_values, 0, columnFrom, 0, ExcelPackage.MaxColumns); | 
|  | foreach (var column in csec) | 
|  | { | 
|  | if (column is ExcelColumn) | 
|  | { | 
|  | var c = (ExcelColumn)column; | 
|  | if (c._columnMin >= columnFrom) | 
|  | { | 
|  | c._columnMin -= columns; | 
|  | c._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]); | 
|  | } | 
|  | } | 
|  | tbl._cols = new ExcelTableColumnCollection(tbl); | 
|  | } | 
|  |  | 
|  | tbl.Address = tbl.Address.DeleteColumn(columnFrom, columns); | 
|  | } | 
|  | } | 
|  | } | 
|  | internal void AdjustFormulasRow(int rowFrom, int rows) | 
|  | { | 
|  | var delSF = new List<int>(); | 
|  | foreach (var sf in _sharedFormulas.Values) | 
|  | { | 
|  | var a = new ExcelAddress(sf.Address).DeleteRow(rowFrom, rows); | 
|  | if (a==null) | 
|  | { | 
|  | delSF.Add(sf.Index); | 
|  | } | 
|  | else | 
|  | { | 
|  | sf.Address = a.Address; | 
|  | if (sf.StartRow > rowFrom) | 
|  | { | 
|  | var r = Math.Min(sf.StartRow - rowFrom, rows); | 
|  | sf.Formula = ExcelCellBase.UpdateFormulaReferences(sf.Formula, -r, 0, rowFrom, 0); | 
|  | sf.StartRow -= r; | 
|  | } | 
|  | } | 
|  | } | 
|  | foreach (var ix in delSF) | 
|  | { | 
|  | _sharedFormulas.Remove(ix); | 
|  | } | 
|  | delSF = null; | 
|  | var cse = new CellsStoreEnumerator<object>(_formulas, 1, 1, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); | 
|  | while (cse.Next()) | 
|  | { | 
|  | if (cse.Value is string) | 
|  | { | 
|  | cse.Value = ExcelCellBase.UpdateFormulaReferences(cse.Value.ToString(), -rows, 0, rowFrom, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | internal void AdjustFormulasColumn(int columnFrom, int columns) | 
|  | { | 
|  | var delSF = new List<int>(); | 
|  | foreach (var sf in _sharedFormulas.Values) | 
|  | { | 
|  | var a = new ExcelAddress(sf.Address).DeleteColumn(columnFrom, columns); | 
|  | if (a == null) | 
|  | { | 
|  | delSF.Add(sf.Index); | 
|  | } | 
|  | else | 
|  | { | 
|  | sf.Address = a.Address; | 
|  | //sf.Formula = ExcelCellBase.UpdateFormulaReferences(sf.Formula, 0, -columns, 0, columnFrom); | 
|  | if (sf.StartCol > columnFrom) | 
|  | { | 
|  | var c = Math.Min(sf.StartCol - columnFrom, columns); | 
|  | sf.Formula = ExcelCellBase.UpdateFormulaReferences(sf.Formula, 0, -c, 0, 1); | 
|  | sf.StartCol-= c; | 
|  | } | 
|  |  | 
|  | //sf.Address = a.Address; | 
|  | //sf.Formula = ExcelCellBase.UpdateFormulaReferences(sf.Formula, 0,-columns,0, columnFrom); | 
|  | //if (sf.StartCol >= columnFrom) | 
|  | //{ | 
|  | //    sf.StartCol -= sf.StartCol; | 
|  | //} | 
|  | } | 
|  | } | 
|  | foreach (var ix in delSF) | 
|  | { | 
|  | _sharedFormulas.Remove(ix); | 
|  | } | 
|  | delSF = null; | 
|  | var cse = new CellsStoreEnumerator<object>(_formulas, 1, 1,  ExcelPackage.MaxRows, ExcelPackage.MaxColumns); | 
|  | while (cse.Next()) | 
|  | { | 
|  | if (cse.Value is string) | 
|  | { | 
|  | cse.Value = ExcelCellBase.UpdateFormulaReferences(cse.Value.ToString(), 0, -columns, 0, columnFrom); | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Deletes the specified row from the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="rowFrom">The number of the start row to be deleted</param> | 
|  | /// <param name="rows">Number of rows to delete</param> | 
|  | /// <param name="shiftOtherRowsUp">Not used. Rows are always shifted</param> | 
|  | public void DeleteRow(int rowFrom, int rows, bool shiftOtherRowsUp) | 
|  | { | 
|  | DeleteRow(rowFrom, rows); | 
|  | } | 
|  | #endregion | 
|  | /// <summary> | 
|  | /// Get the cell value from thw worksheet | 
|  | /// </summary> | 
|  | /// <param name="Row">The row number</param> | 
|  | /// <param name="Column">The row number</param> | 
|  | /// <returns>The value</returns> | 
|  | public object GetValue(int Row, int Column) | 
|  | { | 
|  | CheckSheetType(); | 
|  | //ulong cellID = ExcelCellBase.GetCellID(SheetID, Row, Column); | 
|  | var v = _values.GetValue(Row, Column); | 
|  | if (v!=null) | 
|  | { | 
|  | //var cell = ((ExcelCell)_cells[cellID]); | 
|  | if (_flags.GetFlagValue(Row, Column, CellFlags.RichText)) | 
|  | { | 
|  | return (object)Cells[Row, Column].RichText.Text; | 
|  | } | 
|  | else | 
|  | { | 
|  | return v; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Get a strongly typed cell value from the worksheet | 
|  | /// </summary> | 
|  | /// <typeparam name="T">The type</typeparam> | 
|  | /// <param name="Row">The row number</param> | 
|  | /// <param name="Column">The row number</param> | 
|  | /// <returns>The value. If the value can't be converted to the specified type, the default value will be returned</returns> | 
|  | public T GetValue<T>(int Row, int Column) | 
|  | { | 
|  | CheckSheetType(); | 
|  | //ulong cellID=ExcelCellBase.GetCellID(SheetID, Row, Column); | 
|  | var v = _values.GetValue(Row, Column); | 
|  | if (v==null) | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  |  | 
|  | //var cell=((ExcelCell)_cells[cellID]); | 
|  | if (_flags.GetFlagValue(Row, Column, CellFlags.RichText)) | 
|  | { | 
|  | return (T)(object)Cells[Row, Column].RichText.Text; | 
|  | } | 
|  | else | 
|  | { | 
|  | return GetTypedValue<T>(v); | 
|  | } | 
|  | } | 
|  | //Thanks to Michael Tran for parts of this method | 
|  | internal T GetTypedValue<T>(object v) | 
|  | { | 
|  | if (v == null) | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  | Type fromType = v.GetType(); | 
|  | Type toType = typeof(T); | 
|  | if (fromType == toType) | 
|  | { | 
|  | return (T)v; | 
|  | } | 
|  | var cnv = TypeDescriptor.GetConverter(fromType); | 
|  | if (toType == typeof(DateTime))    //Handle dates | 
|  | { | 
|  | if (fromType == typeof(TimeSpan)) | 
|  | { | 
|  | return ((T)(object)(new DateTime(((TimeSpan)v).Ticks))); | 
|  | } | 
|  | else if (fromType == typeof(string)) | 
|  | { | 
|  | DateTime dt; | 
|  | if (DateTime.TryParse(v.ToString(), out dt)) | 
|  | { | 
|  | return (T)(object)(dt); | 
|  | } | 
|  | else | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  |  | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cnv.CanConvertTo(typeof(double))) | 
|  | { | 
|  | return (T)(object)(DateTime.FromOADate((double)cnv.ConvertTo(v, typeof(double)))); | 
|  | } | 
|  | else | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (toType == typeof(TimeSpan))    //Handle timespan | 
|  | { | 
|  | if (fromType == typeof(DateTime)) | 
|  | { | 
|  | return ((T)(object)(new TimeSpan(((DateTime)v).Ticks))); | 
|  | } | 
|  | else if (fromType == typeof(string)) | 
|  | { | 
|  | TimeSpan ts; | 
|  | if (TimeSpan.TryParse(v.ToString(), out ts)) | 
|  | { | 
|  | return (T)(object)(ts); | 
|  | } | 
|  | else | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cnv.CanConvertTo(typeof(double))) | 
|  | { | 
|  |  | 
|  | return (T)(object)(new TimeSpan(DateTime.FromOADate((double)cnv.ConvertTo(v, typeof(double))).Ticks)); | 
|  | } | 
|  | else | 
|  | { | 
|  | try | 
|  | { | 
|  | // Issue 14682 -- "GetValue<decimal>() won't convert strings" | 
|  | // As suggested, after all special cases, all .NET to do it's | 
|  | // preferred conversion rather than simply returning the default | 
|  | return (T)Convert.ChangeType(v, typeof(T)); | 
|  | } | 
|  | catch (Exception) | 
|  | { | 
|  | // This was the previous behaviour -- no conversion is available. | 
|  | return default(T); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cnv.CanConvertTo(toType)) | 
|  | { | 
|  | return (T)cnv.ConvertTo(v, typeof(T)); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) | 
|  | { | 
|  | toType = Nullable.GetUnderlyingType(toType); | 
|  | if (cnv.CanConvertTo(toType)) | 
|  | { | 
|  | return (T)cnv.ConvertTo(v, typeof(T)); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(fromType==typeof(double) && toType==typeof(decimal)) | 
|  | { | 
|  | return (T)(object)Convert.ToDecimal(v); | 
|  | } | 
|  | else if (fromType == typeof(decimal) && toType == typeof(double)) | 
|  | { | 
|  | return (T)(object)Convert.ToDouble(v); | 
|  | } | 
|  | else | 
|  | { | 
|  | return default(T); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Set the value of a cell | 
|  | /// </summary> | 
|  | /// <param name="Row">The row number</param> | 
|  | /// <param name="Column">The column number</param> | 
|  | /// <param name="Value">The value</param> | 
|  | public void SetValue(int Row, int Column, object Value) | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (Row < 1 || Column < 1 || Row > ExcelPackage.MaxRows && Column > ExcelPackage.MaxColumns) | 
|  | { | 
|  | throw new ArgumentOutOfRangeException("Row or Column out of range"); | 
|  | } | 
|  | _values.SetValue(Row, Column, Value); | 
|  | } | 
|  | /// <summary> | 
|  | /// Set the value of a cell | 
|  | /// </summary> | 
|  | /// <param name="Address">The Excel address</param> | 
|  | /// <param name="Value">The value</param> | 
|  | public void SetValue(string Address, object Value) | 
|  | { | 
|  | CheckSheetType(); | 
|  | int row, col; | 
|  | ExcelAddressBase.GetRowCol(Address, out row, out col, true); | 
|  | if (row < 1 || col < 1 || row > ExcelPackage.MaxRows && col > ExcelPackage.MaxColumns) | 
|  | { | 
|  | throw new ArgumentOutOfRangeException("Address is invalid or out of range"); | 
|  | } | 
|  | _values.SetValue(row, col, Value); | 
|  | } | 
|  |  | 
|  | #region MergeCellId | 
|  |  | 
|  | /// <summary> | 
|  | /// Get MergeCell Index No | 
|  | /// </summary> | 
|  | /// <param name="row"></param> | 
|  | /// <param name="column"></param> | 
|  | /// <returns></returns> | 
|  | public int GetMergeCellId(int row, int column) | 
|  | { | 
|  | for (int i = 0; i < _mergedCells.Count; i++) | 
|  | { | 
|  | if(!string.IsNullOrEmpty( _mergedCells[i])) | 
|  | { | 
|  | ExcelRange range = Cells[_mergedCells[i]]; | 
|  |  | 
|  | if (range.Start.Row <= row && row <= range.End.Row) | 
|  | { | 
|  | if (range.Start.Column <= column && column <= range.End.Column) | 
|  | { | 
|  | return i + 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endregion | 
|  | #endregion // END Worksheet Public Methods | 
|  |  | 
|  | #region Worksheet Private Methods | 
|  |  | 
|  | #region Worksheet Save | 
|  | 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) | 
|  | { | 
|  | this.DeleteAllNode("d:dimension/@ref"); | 
|  | } | 
|  | else | 
|  | { | 
|  | this.SetXmlNodeString("d:dimension/@ref", d.Address); | 
|  | } | 
|  |  | 
|  |  | 
|  | if (Drawings.Count == 0) | 
|  | { | 
|  | //Remove node if no drawings exists. | 
|  | DeleteNode("d:drawing"); | 
|  | } | 
|  |  | 
|  | SaveComments(); | 
|  | HeaderFooter.SaveHeaderFooterImages(); | 
|  | SaveTables(); | 
|  | SavePivotTables(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (Drawings.UriDrawing!=null) | 
|  | { | 
|  | if (Drawings.Count == 0) | 
|  | { | 
|  | Part.DeleteRelationship(Drawings._drawingRelation.Id); | 
|  | _package.Package.DeletePart(Drawings.UriDrawing); | 
|  | } | 
|  | else | 
|  | { | 
|  | Packaging.ZipPackagePart partPack = Drawings.Part; | 
|  | Drawings.DrawingXml.Save(partPack.GetStream(FileMode.Create, FileAccess.Write)); | 
|  | foreach (ExcelDrawing d in Drawings) | 
|  | { | 
|  | if (d is ExcelChart) | 
|  | { | 
|  | ExcelChart c = (ExcelChart)d; | 
|  | c.ChartXml.Save(c.Part.GetStream(FileMode.Create, FileAccess.Write)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | internal void SaveHandler(ZipOutputStream stream, CompressionLevel compressionLevel, string fileName) | 
|  | { | 
|  | //Init Zip | 
|  | stream.CodecBufferSize = 8096; | 
|  | stream.CompressionLevel = (OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel)compressionLevel; | 
|  | stream.PutNextEntry(fileName); | 
|  |  | 
|  |  | 
|  | SaveXml(stream); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | ///// <summary> | 
|  | ///// Saves the worksheet to the package. | 
|  | ///// </summary> | 
|  | //internal void Save()  // Worksheet Save | 
|  | //{ | 
|  | //    DeletePrinterSettings(); | 
|  |  | 
|  | //    if (_worksheetXml != null) | 
|  | //    { | 
|  |  | 
|  | //        // save the header & footer (if defined) | 
|  | //        if (_headerFooter != null) | 
|  | //            HeaderFooter.Save(); | 
|  |  | 
|  | //        var d = Dimension; | 
|  | //        if (d == null) | 
|  | //        { | 
|  | //            this.DeleteAllNode("d:dimension/@ref"); | 
|  | //        } | 
|  | //        else | 
|  | //        { | 
|  | //            this.SetXmlNodeString("d:dimension/@ref", d.Address); | 
|  | //        } | 
|  |  | 
|  |  | 
|  | //        if (_drawings != null && _drawings.Count == 0) | 
|  | //        { | 
|  | //            //Remove node if no drawings exists. | 
|  | //            DeleteNode("d:drawing"); | 
|  | //        } | 
|  |  | 
|  | //        SaveComments(); | 
|  | //        HeaderFooter.SaveHeaderFooterImages(); | 
|  | //        SaveTables(); | 
|  | //        SavePivotTables(); | 
|  | //        SaveXml(); | 
|  | //    } | 
|  |  | 
|  | //    if (Drawings.UriDrawing!=null) | 
|  | //    { | 
|  | //        if (Drawings.Count == 0) | 
|  | //        { | 
|  | //            Part.DeleteRelationship(Drawings._drawingRelation.Id); | 
|  | //            _package.Package.DeletePart(Drawings.UriDrawing); | 
|  | //        } | 
|  | //        else | 
|  | //        { | 
|  | //            Packaging.ZipPackagePart partPack = Drawings.Part; | 
|  | //            Drawings.DrawingXml.Save(partPack.GetStream(FileMode.Create, FileAccess.Write)); | 
|  | //            foreach (ExcelDrawing d in Drawings) | 
|  | //            { | 
|  | //                if (d is ExcelChart) | 
|  | //                { | 
|  | //                    ExcelChart c = (ExcelChart)d; | 
|  | //                    c.ChartXml.Save(c.Part.GetStream(FileMode.Create, FileAccess.Write)); | 
|  | //                } | 
|  | //            } | 
|  | //        } | 
|  | //    } | 
|  | //} | 
|  |  | 
|  | /// <summary> | 
|  | /// Delete the printersettings relationship and part. | 
|  | /// </summary> | 
|  | private void DeletePrinterSettings() | 
|  | { | 
|  | //Delete the relationship from the pageSetup tag | 
|  | XmlAttribute attr = (XmlAttribute)WorksheetXml.SelectSingleNode("//d:pageSetup/@r:id", NameSpaceManager); | 
|  | if (attr != null) | 
|  | { | 
|  | string relID = attr.Value; | 
|  | //First delete the attribute from the XML | 
|  | attr.OwnerElement.Attributes.Remove(attr); | 
|  | if(Part.RelationshipExists(relID)) | 
|  | { | 
|  | var rel = Part.GetRelationship(relID); | 
|  | Uri printerSettingsUri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri); | 
|  | Part.DeleteRelationship(rel.Id); | 
|  |  | 
|  | //Delete the part from the package | 
|  | if(_package.Package.PartExists(printerSettingsUri)) | 
|  | { | 
|  | _package.Package.DeletePart(printerSettingsUri); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | 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 Uri(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), Packaging.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 = XmlHelper.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), Packaging.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> | 
|  | private void SaveTables() | 
|  | { | 
|  | foreach (var tbl in Tables) | 
|  | { | 
|  | if (tbl.ShowHeader || tbl.ShowTotal) | 
|  | { | 
|  | int colNum = tbl.Address._fromCol; | 
|  | var colVal = new HashSet<string>(); | 
|  | foreach (var col in tbl.Columns) | 
|  | { | 
|  | string n=col.Name.ToLower(CultureInfo.InvariantCulture); | 
|  | if (tbl.ShowHeader) | 
|  | { | 
|  | n = tbl.WorkSheet.GetValue<string>(tbl.Address._fromRow, | 
|  | tbl.Address._fromCol + col.Position); | 
|  | if (string.IsNullOrEmpty(n)) | 
|  | { | 
|  | n = col.Name.ToLower(CultureInfo.InvariantCulture); | 
|  | } | 
|  | else | 
|  | { | 
|  | col.Name = n; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | n = col.Name.ToLower(CultureInfo.InvariantCulture); | 
|  | } | 
|  |  | 
|  | if(colVal.Contains(n)) | 
|  | { | 
|  | throw(new InvalidDataException(string.Format("Table {0} Column {1} does not have a unique name.", tbl.Name, col.Name))); | 
|  | } | 
|  | colVal.Add(n); | 
|  | col.Name = ConvertUtil.ExcelEncodeString(col.Name); | 
|  | if (tbl.ShowHeader) | 
|  | { | 
|  | _values.SetValue(tbl.Address._fromRow, colNum, col.Name); | 
|  | } | 
|  | if (tbl.ShowTotal) | 
|  | { | 
|  | SetTableTotalFunction(tbl, col, colNum); | 
|  | } | 
|  | if (!string.IsNullOrEmpty(col.CalculatedColumnFormula)) | 
|  | { | 
|  | int fromRow = tbl.ShowHeader ? tbl.Address._fromRow + 1 : tbl.Address._fromRow; | 
|  | int toRow = tbl.ShowTotal ? tbl.Address._toRow - 1 : tbl.Address._toRow; | 
|  | for (int row = fromRow; row <= toRow; row++) | 
|  | { | 
|  | //Cell(row, colNum).Formula = col.CalculatedColumnFormula; | 
|  | SetFormula(row, colNum, col.CalculatedColumnFormula); | 
|  | } | 
|  | } | 
|  | 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), Packaging.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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | internal void SetTableTotalFunction(ExcelTable tbl, ExcelTableColumn col, int colNum=-1) | 
|  | { | 
|  | if (tbl.ShowTotal == false) return; | 
|  | if (colNum == -1) | 
|  | { | 
|  | for (int i = 0; i < tbl.Columns.Count; i++) | 
|  | { | 
|  | if (tbl.Columns[i].Name == col.Name) | 
|  | { | 
|  | colNum = tbl.Address._fromCol + i; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (col.TotalsRowFunction == RowFunctions.Custom) | 
|  | { | 
|  | SetFormula(tbl.Address._toRow, colNum, col.TotalsRowFormula); | 
|  | } | 
|  | else if (col.TotalsRowFunction != RowFunctions.None) | 
|  | { | 
|  | switch (col.TotalsRowFunction) | 
|  | { | 
|  | case RowFunctions.Average: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "101")); | 
|  | break; | 
|  | case RowFunctions.Count: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "102")); | 
|  | break; | 
|  | case RowFunctions.CountNums: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "103")); | 
|  | break; | 
|  | case RowFunctions.Max: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "104")); | 
|  | break; | 
|  | case RowFunctions.Min: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "105")); | 
|  | break; | 
|  | case RowFunctions.StdDev: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "107")); | 
|  | break; | 
|  | case RowFunctions.Var: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "110")); | 
|  | break; | 
|  | case RowFunctions.Sum: | 
|  | SetFormula(tbl.Address._toRow, colNum, GetTotalFunction(col, "109")); | 
|  | break; | 
|  | default: | 
|  | throw (new Exception("Unknown RowFunction enum")); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | _values.SetValue(tbl.Address._toRow, colNum, col.TotalsRowLabel); | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | internal void SetFormula(int row, int col, object value) | 
|  | { | 
|  | _formulas.SetValue(row, col, value); | 
|  | if (!_values.Exists(row, col)) _values.SetValue(row, col, null); | 
|  | } | 
|  | internal void SetStyle(int row, int col, int value) | 
|  | { | 
|  | _styles.SetValue(row, col, value); | 
|  | if(!_values.Exists(row,col)) _values.SetValue(row, col, null); | 
|  | } | 
|  |  | 
|  | private void SavePivotTables() | 
|  | { | 
|  | foreach (var pt in PivotTables) | 
|  | { | 
|  | if (pt.DataFields.Count > 1) | 
|  | { | 
|  | XmlElement parentNode; | 
|  | if(pt.DataOnRows==true) | 
|  | { | 
|  | parentNode =  pt.PivotTableXml.SelectSingleNode("//d:rowFields", pt.NameSpaceManager) as XmlElement; | 
|  | if (parentNode == null) | 
|  | { | 
|  | pt.CreateNode("d:rowFields"); | 
|  | parentNode = pt.PivotTableXml.SelectSingleNode("//d:rowFields", pt.NameSpaceManager) as XmlElement; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | parentNode =  pt.PivotTableXml.SelectSingleNode("//d:colFields", pt.NameSpaceManager) as XmlElement; | 
|  | if (parentNode == null) | 
|  | { | 
|  | pt.CreateNode("d:colFields"); | 
|  | parentNode = pt.PivotTableXml.SelectSingleNode("//d:colFields", pt.NameSpaceManager) as XmlElement; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (parentNode.SelectSingleNode("d:field[@ x= \"-2\"]", pt.NameSpaceManager) == null) | 
|  | { | 
|  | XmlElement fieldNode = pt.PivotTableXml.CreateElement("field", ExcelPackage.schemaMain); | 
|  | fieldNode.SetAttribute("x", "-2"); | 
|  | parentNode.AppendChild(fieldNode); | 
|  | } | 
|  | } | 
|  | var ws = Workbook.Worksheets[pt.CacheDefinition.SourceRange.WorkSheet]; | 
|  | var t = ws.Tables.GetFromRange(pt.CacheDefinition.SourceRange); | 
|  | var fields = | 
|  | pt.CacheDefinition.CacheDefinitionXml.SelectNodes( | 
|  | "d:pivotCacheDefinition/d:cacheFields/d:cacheField", NameSpaceManager); | 
|  | int ix = 0; | 
|  | if (fields != null) | 
|  | { | 
|  | var flds = new HashSet<string>(); | 
|  | foreach (XmlElement node in fields) | 
|  | { | 
|  | if (ix >= pt.CacheDefinition.SourceRange.Columns) break; | 
|  | var fldName = node.GetAttribute("name");                        //Fixes issue 15295 dup name error | 
|  | if (string.IsNullOrEmpty(fldName)) | 
|  | { | 
|  | fldName = (t == null | 
|  | ? pt.CacheDefinition.SourceRange.Offset(0, ix++, 1, 1).Value.ToString() | 
|  | : t.Columns[ix++].Name); | 
|  | } | 
|  | if (flds.Contains(fldName)) | 
|  | { | 
|  | fldName = GetNewName(flds, fldName); | 
|  | } | 
|  | flds.Add(fldName); | 
|  | node.SetAttribute("name", fldName); | 
|  | } | 
|  | foreach (var df in pt.DataFields) | 
|  | { | 
|  | if (string.IsNullOrEmpty(df.Name)) | 
|  | { | 
|  | string name; | 
|  | if (df.Function == DataFieldFunctions.None) | 
|  | { | 
|  | name = df.Field.Name; //Name must be set or Excel will crash on rename. | 
|  | } | 
|  | else | 
|  | { | 
|  | name = df.Function.ToString() + " of " + df.Field.Name; //Name must be set or Excel will crash on rename. | 
|  | } | 
|  | //Make sure name is unique | 
|  | var newName = name; | 
|  | var i = 2; | 
|  | while (pt.DataFields.ExistsDfName(newName, df)) | 
|  | { | 
|  | newName = name + (i++).ToString(CultureInfo.InvariantCulture); | 
|  | } | 
|  | df.Name = newName; | 
|  | } | 
|  | } | 
|  | } | 
|  | pt.PivotTableXml.Save(pt.Part.GetStream(FileMode.Create)); | 
|  | pt.CacheDefinition.CacheDefinitionXml.Save(pt.CacheDefinition.Part.GetStream(FileMode.Create)); | 
|  | } | 
|  | } | 
|  |  | 
|  | private string GetNewName(HashSet<string> flds, string fldName) | 
|  | { | 
|  | int ix = 2; | 
|  | while (flds.Contains(fldName + ix.ToString(CultureInfo.InvariantCulture))) | 
|  | { | 
|  | ix++; | 
|  | } | 
|  | return fldName + ix.ToString(CultureInfo.InvariantCulture); | 
|  | } | 
|  |  | 
|  | private static string GetTotalFunction(ExcelTableColumn col,string FunctionNum) | 
|  | { | 
|  | return string.Format("SUBTOTAL({0},{1}[{2}])", FunctionNum, col._tbl.Name, col.Name); | 
|  | } | 
|  | private void SaveXml(Stream stream) | 
|  | { | 
|  | //Create the nodes if they do not exist. | 
|  | StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8, 65536); | 
|  | if (this is ExcelChartsheet) | 
|  | { | 
|  | sw.Write(_worksheetXml.OuterXml); | 
|  | } | 
|  | else | 
|  | { | 
|  | CreateNode("d:cols"); | 
|  | CreateNode("d:sheetData"); | 
|  | CreateNode("d:mergeCells"); | 
|  | CreateNode("d:hyperlinks"); | 
|  | CreateNode("d:rowBreaks"); | 
|  | CreateNode("d:colBreaks"); | 
|  |  | 
|  | //StreamWriter sw=new StreamWriter(Part.GetStream(FileMode.Create, FileAccess.Write)); | 
|  | var xml = _worksheetXml.OuterXml; | 
|  | int colStart = 0, colEnd = 0; | 
|  | GetBlockPos(xml, "cols", ref colStart, ref colEnd); | 
|  |  | 
|  | sw.Write(xml.Substring(0, colStart)); | 
|  | var colBreaks = new List<int>(); | 
|  | //if (_columns.Count > 0) | 
|  | //{ | 
|  | UpdateColumnData(sw); | 
|  | //} | 
|  |  | 
|  | int cellStart = colEnd, cellEnd = colEnd; | 
|  | GetBlockPos(xml, "sheetData", ref cellStart, ref cellEnd); | 
|  |  | 
|  | sw.Write(xml.Substring(colEnd, cellStart - colEnd)); | 
|  | var rowBreaks = new List<int>(); | 
|  | UpdateRowCellData(sw); | 
|  |  | 
|  | int mergeStart = cellEnd, mergeEnd = cellEnd; | 
|  |  | 
|  | GetBlockPos(xml, "mergeCells", ref mergeStart, ref mergeEnd); | 
|  | sw.Write(xml.Substring(cellEnd, mergeStart - cellEnd)); | 
|  |  | 
|  | CleanupMergedCells(_mergedCells); | 
|  | if (_mergedCells.Count > 0) | 
|  | { | 
|  | UpdateMergedCells(sw); | 
|  | } | 
|  |  | 
|  | int hyperStart = mergeEnd, hyperEnd = mergeEnd; | 
|  | GetBlockPos(xml, "hyperlinks", ref hyperStart, ref hyperEnd); | 
|  | sw.Write(xml.Substring(mergeEnd, hyperStart - mergeEnd)); | 
|  | //if (_hyperLinkCells.Count > 0) | 
|  | //{ | 
|  | UpdateHyperLinks(sw); | 
|  | // } | 
|  |  | 
|  | int rowBreakStart = hyperEnd, rowBreakEnd = hyperEnd; | 
|  | GetBlockPos(xml, "rowBreaks", ref rowBreakStart, ref rowBreakEnd); | 
|  | sw.Write(xml.Substring(hyperEnd, rowBreakStart - hyperEnd)); | 
|  | //if (rowBreaks.Count > 0) | 
|  | //{ | 
|  | UpdateRowBreaks(sw); | 
|  | //} | 
|  |  | 
|  | int colBreakStart = rowBreakEnd, colBreakEnd = rowBreakEnd; | 
|  | GetBlockPos(xml, "colBreaks", ref colBreakStart, ref colBreakEnd); | 
|  | sw.Write(xml.Substring(rowBreakEnd, colBreakStart - rowBreakEnd)); | 
|  | //if (colBreaks.Count > 0) | 
|  | //{ | 
|  | UpdateColBreaks(sw); | 
|  | //} | 
|  | sw.Write(xml.Substring(colBreakEnd, xml.Length - colBreakEnd)); | 
|  | } | 
|  | sw.Flush(); | 
|  | //sw.Close(); | 
|  | } | 
|  |  | 
|  | private void CleanupMergedCells(MergeCellsCollection _mergedCells) | 
|  | { | 
|  | int i=0; | 
|  | while (i < _mergedCells.List.Count) | 
|  | { | 
|  | if (_mergedCells[i] == null) | 
|  | { | 
|  | _mergedCells.List.RemoveAt(i); | 
|  | } | 
|  | else | 
|  | { | 
|  | i++; | 
|  | } | 
|  | } | 
|  | } | 
|  | private void UpdateColBreaks(StreamWriter sw) | 
|  | { | 
|  | StringBuilder breaks = new StringBuilder(); | 
|  | int count = 0; | 
|  | var cse = new CellsStoreEnumerator<object>(_values, 0, 0, 0, ExcelPackage.MaxColumns); | 
|  | //foreach (ExcelColumn col in _columns) | 
|  | while(cse.Next()) | 
|  | { | 
|  | var col=cse.Value as ExcelColumn; | 
|  | if (col != null && col.PageBreak) | 
|  | { | 
|  | breaks.AppendFormat("<brk id=\"{0}\" max=\"16383\" man=\"1\" />", cse.Column); | 
|  | count++; | 
|  | } | 
|  | } | 
|  | if (count > 0) | 
|  | { | 
|  | sw.Write(string.Format("<colBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</colBreaks>", count, breaks.ToString())); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void UpdateRowBreaks(StreamWriter sw) | 
|  | { | 
|  | StringBuilder breaks=new StringBuilder(); | 
|  | int count = 0; | 
|  | var cse = new CellsStoreEnumerator<object>(_values, 0, 0, ExcelPackage.MaxRows, 0); | 
|  | //foreach(ExcelRow row in _rows) | 
|  | while(cse.Next()) | 
|  | { | 
|  | var row=cse.Value as RowInternal; | 
|  | if (row != null && row.PageBreak) | 
|  | { | 
|  | breaks.AppendFormat("<brk id=\"{0}\" max=\"1048575\" man=\"1\" />", cse.Row); | 
|  | count++; | 
|  | } | 
|  | } | 
|  | if (count>0) | 
|  | { | 
|  | sw.Write(string.Format("<rowBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</rowBreaks>", count, breaks.ToString())); | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Inserts the cols collection into the XML document | 
|  | /// </summary> | 
|  | private void UpdateColumnData(StreamWriter sw) | 
|  | { | 
|  | //ExcelColumn prevCol = null;   //commented out 11/1-12 JK | 
|  | //foreach (ExcelColumn col in _columns) | 
|  | //{ | 
|  | //    if (prevCol != null) | 
|  | //    { | 
|  | //        if(prevCol.ColumnMax != col.ColumnMin-1) | 
|  | //        { | 
|  | //            prevCol._columnMax=col.ColumnMin-1; | 
|  | //        } | 
|  | //    } | 
|  | //    prevCol = col; | 
|  | //} | 
|  | var cse = new CellsStoreEnumerator<object>(_values, 0, 1, 0, ExcelPackage.MaxColumns); | 
|  | //sw.Write("<cols>"); | 
|  | //foreach (ExcelColumn col in _columns) | 
|  | bool first = true; | 
|  | while(cse.Next()) | 
|  | { | 
|  | if (first) | 
|  | { | 
|  | sw.Write("<cols>"); | 
|  | first = false; | 
|  | } | 
|  | var col = cse.Value as ExcelColumn; | 
|  | ExcelStyleCollection<ExcelXfs> cellXfs = _package.Workbook.Styles.CellXfs; | 
|  |  | 
|  | sw.Write("<col min=\"{0}\" max=\"{1}\"", col.ColumnMin, col.ColumnMax); | 
|  | if (col.Hidden == true) | 
|  | { | 
|  | //sbXml.Append(" width=\"0\" hidden=\"1\" customWidth=\"1\""); | 
|  | sw.Write(" hidden=\"1\""); | 
|  | } | 
|  | else if (col.BestFit) | 
|  | { | 
|  | sw.Write(" bestFit=\"1\""); | 
|  | } | 
|  | sw.Write(string.Format(CultureInfo.InvariantCulture, " width=\"{0}\" customWidth=\"1\"", col.Width)); | 
|  | if (col.OutlineLevel > 0) | 
|  | { | 
|  | sw.Write(" outlineLevel=\"{0}\" ", col.OutlineLevel); | 
|  | if (col.Collapsed) | 
|  | { | 
|  | if (col.Hidden) | 
|  | { | 
|  | sw.Write(" collapsed=\"1\""); | 
|  | } | 
|  | else | 
|  | { | 
|  | sw.Write(" collapsed=\"1\" hidden=\"1\""); //Always hidden | 
|  | } | 
|  | } | 
|  | } | 
|  | if (col.Phonetic) | 
|  | { | 
|  | sw.Write(" phonetic=\"1\""); | 
|  | } | 
|  |  | 
|  | var styleID = col.StyleID >= 0 ? cellXfs[col.StyleID].newID : col.StyleID; | 
|  | if (styleID > 0) | 
|  | { | 
|  | sw.Write(" style=\"{0}\"", styleID); | 
|  | } | 
|  | sw.Write(" />"); | 
|  |  | 
|  | //if (col.PageBreak) | 
|  | //{ | 
|  | //    colBreaks.Add(col.ColumnMin); | 
|  | //} | 
|  | } | 
|  | if (!first) | 
|  | { | 
|  | sw.Write("</cols>"); | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Insert row and cells into the XML document | 
|  | /// </summary> | 
|  | private void UpdateRowCellData(StreamWriter sw) | 
|  | { | 
|  | ExcelStyleCollection<ExcelXfs> cellXfs = _package.Workbook.Styles.CellXfs; | 
|  |  | 
|  | int row = -1; | 
|  |  | 
|  | StringBuilder sbXml = new StringBuilder(); | 
|  | var ss = _package.Workbook._sharedStrings; | 
|  | var styles = _package.Workbook.Styles; | 
|  | var cache = new StringBuilder(); | 
|  | cache.Append("<sheetData>"); | 
|  |  | 
|  | //Set a value for cells with style and no value set. | 
|  | var cseStyle = new CellsStoreEnumerator<int>(_styles, 0, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); | 
|  | foreach (var s in cseStyle) | 
|  | { | 
|  | if(!_values.Exists(cseStyle.Row, cseStyle.Column)) | 
|  | { | 
|  | _values.SetValue(cseStyle.Row, cseStyle.Column, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | var cse = new CellsStoreEnumerator<object>(_values, 1, 0, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); | 
|  | //foreach (IRangeID r in _cells) | 
|  | while(cse.Next()) | 
|  | { | 
|  | if (cse.Column>0) | 
|  | { | 
|  | int styleID = cellXfs[styles.GetStyleId(this, cse.Row, cse.Column)].newID; | 
|  | //Add the row element if it's a new row | 
|  | if (cse.Row != row) | 
|  | { | 
|  | WriteRow(cache, cellXfs, row, cse.Row); | 
|  | row = cse.Row; | 
|  | } | 
|  | object v = cse.Value; | 
|  | object formula = _formulas.GetValue(cse.Row, cse.Column); | 
|  | if (formula is int) | 
|  | { | 
|  | int sfId = (int)formula; | 
|  | var f = _sharedFormulas[(int)sfId]; | 
|  | if (f.Address.IndexOf(':') > 0) | 
|  | { | 
|  | if (f.StartCol == cse.Column && f.StartRow == cse.Row) | 
|  | { | 
|  | if (f.IsArray) | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{5}><f ref=\"{2}\" t=\"array\">{3}</f>{4}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, SecurityElement.Escape(f.Formula), GetFormulaValue(v), GetCellType(v,true)); | 
|  | } | 
|  | else | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{6}><f ref=\"{2}\" t=\"shared\"  si=\"{3}\">{4}</f>{5}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, SecurityElement.Escape(f.Formula), GetFormulaValue(v), GetCellType(v,true)); | 
|  | } | 
|  |  | 
|  | } | 
|  | else if (f.IsArray) | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\" />", cse.CellAddress, styleID < 0 ? 0 : styleID); | 
|  | } | 
|  | else | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{4}><f t=\"shared\" si=\"{2}\" />{3}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v,true)); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // We can also have a single cell array formula | 
|  | if(f.IsArray) | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{5}><f ref=\"{2}\" t=\"array\">{3}</f>{4}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), SecurityElement.Escape(f.Formula), GetFormulaValue(v), GetCellType(v,true)); | 
|  | } | 
|  | else | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\">", f.Address, styleID < 0 ? 0 : styleID); | 
|  | cache.AppendFormat("<f>{0}</f>{1}</c>", SecurityElement.Escape(f.Formula), GetFormulaValue(v)); | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (formula!=null && formula.ToString()!="") | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v,true)); | 
|  | cache.AppendFormat("<f>{0}</f>{1}</c>", SecurityElement.Escape(formula.ToString()), GetFormulaValue(v)); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (v == null && styleID > 0) | 
|  | { | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\" />", cse.CellAddress, styleID < 0 ? 0 : styleID); | 
|  | } | 
|  | else if(v != null) | 
|  | { | 
|  | if ((v.GetType().IsPrimitive || v is double || v is decimal || v is DateTime || v is TimeSpan)) | 
|  | { | 
|  | //string sv = GetValueForXml(v); | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\" {2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v)); | 
|  | cache.AppendFormat("{0}</c>", GetFormulaValue(v)); | 
|  | } | 
|  | else | 
|  | { | 
|  | int ix; | 
|  | if (!ss.ContainsKey(v.ToString())) | 
|  | { | 
|  | ix = ss.Count; | 
|  | ss.Add(v.ToString(), new ExcelWorkbook.SharedStringItem() { isRichText = _flags.GetFlagValue(cse.Row,cse.Column,CellFlags.RichText), pos = ix }); | 
|  | } | 
|  | else | 
|  | { | 
|  | ix = ss[v.ToString()].pos; | 
|  | } | 
|  | cache.AppendFormat("<c r=\"{0}\" s=\"{1}\" t=\"s\">", cse.CellAddress, styleID < 0 ? 0 : styleID); | 
|  | cache.AppendFormat("<v>{0}</v></c>", ix); | 
|  | } | 
|  | } | 
|  | } | 
|  | ////Update hyperlinks. | 
|  | //if (cell.Hyperlink != null) | 
|  | //{ | 
|  | //    _hyperLinkCells.Add(cell.CellID); | 
|  | //} | 
|  | } | 
|  | else  //ExcelRow | 
|  | { | 
|  | //int newRow=((ExcelRow)cse.Value).Row; | 
|  | WriteRow(cache, cellXfs, row, cse.Row); | 
|  | row = cse.Row; | 
|  | } | 
|  | if (cache.Length > 0x600000) | 
|  | { | 
|  | sw.Write(cache.ToString()); | 
|  | cache = new StringBuilder(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (row != -1) cache.Append("</row>"); | 
|  | cache.Append("</sheetData>"); | 
|  | sw.Write(cache.ToString()); | 
|  | sw.Flush(); | 
|  | } | 
|  |  | 
|  | private object GetFormulaValue(object v) | 
|  | { | 
|  | //if (_package.Workbook._isCalculated) | 
|  | //{ | 
|  | if (v != null && v.ToString()!="") | 
|  | { | 
|  | return "<v>" + SecurityElement.Escape(GetValueForXml(v)) + "</v>"; //Fixes issue 15071 | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | private string GetCellType(object v, bool allowStr=false) | 
|  | { | 
|  | if (v is bool) | 
|  | { | 
|  | return " t=\"b\""; | 
|  | } | 
|  | else if ((v is double && double.IsInfinity((double)v)) || v is ExcelErrorValue) | 
|  | { | 
|  | return " t=\"e\""; | 
|  | } | 
|  | else if(allowStr && v!=null && !(v.GetType().IsPrimitive || v is double || v is decimal || v is DateTime || v is TimeSpan)) | 
|  | { | 
|  | return " t=\"str\""; | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | private string GetValueForXml(object v) | 
|  | { | 
|  | string s; | 
|  | try | 
|  | { | 
|  | if (v is DateTime) | 
|  | { | 
|  | double sdv = ((DateTime)v).ToOADate(); | 
|  |  | 
|  | if (Workbook.Date1904) | 
|  | { | 
|  | sdv -= ExcelWorkbook.date1904Offset; | 
|  | } | 
|  |  | 
|  | s = sdv.ToString(CultureInfo.InvariantCulture); | 
|  | } | 
|  | else if (v is TimeSpan) | 
|  | { | 
|  | s = new DateTime(((TimeSpan)v).Ticks).ToOADate().ToString(CultureInfo.InvariantCulture); ; | 
|  | } | 
|  | else if(v.GetType().IsPrimitive || v is double || v is decimal) | 
|  | { | 
|  | if (v is double && double.IsNaN((double)v)) | 
|  | { | 
|  | s = ""; | 
|  | } | 
|  | else if (v is double && double.IsInfinity((double)v)) | 
|  | { | 
|  | s = "#NUM!"; | 
|  | } | 
|  | else | 
|  | { | 
|  | s = Convert.ToDouble(v, CultureInfo.InvariantCulture).ToString("R15", CultureInfo.InvariantCulture); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | s = v.ToString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | catch | 
|  | { | 
|  | s = "0"; | 
|  | } | 
|  | return s; | 
|  | } | 
|  | private void WriteRow(StringBuilder cache, ExcelStyleCollection<ExcelXfs> cellXfs, int prevRow, int row) | 
|  | { | 
|  | if (prevRow != -1) cache.Append("</row>"); | 
|  | //ulong rowID = ExcelRow.GetRowID(SheetID, row); | 
|  | cache.AppendFormat("<row r=\"{0}\" ", row); | 
|  | RowInternal currRow = _values.GetValue(row, 0) as RowInternal; | 
|  | if (currRow != null) | 
|  | { | 
|  |  | 
|  | if (currRow.Hidden == true) | 
|  | { | 
|  | cache.Append("ht=\"0\" hidden=\"1\" "); | 
|  | } | 
|  | else if (currRow.Height != DefaultRowHeight && currRow.Height>=0) | 
|  | { | 
|  | cache.AppendFormat(string.Format(CultureInfo.InvariantCulture, "ht=\"{0}\" ", currRow.Height)); | 
|  | if (currRow.CustomHeight) | 
|  | { | 
|  | cache.Append("customHeight=\"1\" "); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (currRow.OutlineLevel > 0) | 
|  | { | 
|  | cache.AppendFormat("outlineLevel =\"{0}\" ", currRow.OutlineLevel); | 
|  | if (currRow.Collapsed) | 
|  | { | 
|  | if (currRow.Hidden) | 
|  | { | 
|  | cache.Append(" collapsed=\"1\" "); | 
|  | } | 
|  | else | 
|  | { | 
|  | cache.Append(" collapsed=\"1\" hidden=\"1\" "); //Always hidden | 
|  | } | 
|  | } | 
|  | } | 
|  | if (currRow.Phonetic) | 
|  | { | 
|  | cache.Append("ph=\"1\" "); | 
|  | } | 
|  | } | 
|  | var s = _styles.GetValue(row, 0); | 
|  | if (s > 0) | 
|  | { | 
|  | cache.AppendFormat("s=\"{0}\" customFormat=\"1\"", cellXfs[s].newID); | 
|  | } | 
|  | cache.Append(">"); | 
|  | } | 
|  | private void WriteRow(StreamWriter sw, ExcelStyleCollection<ExcelXfs> cellXfs, int prevRow, int row) | 
|  | { | 
|  | if (prevRow != -1) sw.Write("</row>"); | 
|  | //ulong rowID = ExcelRow.GetRowID(SheetID, row); | 
|  | sw.Write("<row r=\"{0}\" ", row); | 
|  | RowInternal currRow = _values.GetValue(row, 0) as RowInternal; | 
|  | if (currRow!=null) | 
|  | { | 
|  |  | 
|  | if (currRow.Hidden == true) | 
|  | { | 
|  | sw.Write("ht=\"0\" hidden=\"1\" "); | 
|  | } | 
|  | else if (currRow.Height != DefaultRowHeight) | 
|  | { | 
|  | sw.Write(string.Format(CultureInfo.InvariantCulture, "ht=\"{0}\" ", currRow.Height)); | 
|  | if (currRow.CustomHeight) | 
|  | { | 
|  | sw.Write("customHeight=\"1\" "); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (currRow.OutlineLevel > 0) | 
|  | { | 
|  | sw.Write("outlineLevel =\"{0}\" ", currRow.OutlineLevel); | 
|  | if (currRow.Collapsed) | 
|  | { | 
|  | if (currRow.Hidden) | 
|  | { | 
|  | sw.Write(" collapsed=\"1\" "); | 
|  | } | 
|  | else | 
|  | { | 
|  | sw.Write(" collapsed=\"1\" hidden=\"1\" "); //Always hidden | 
|  | } | 
|  | } | 
|  | } | 
|  | if (currRow.Phonetic) | 
|  | { | 
|  | sw.Write("ph=\"1\" "); | 
|  | } | 
|  | } | 
|  | var s = _styles.GetValue(row, 0); | 
|  | if (s > 0) | 
|  | { | 
|  | sw.Write("s=\"{0}\" customFormat=\"1\"", cellXfs[s].newID); | 
|  | } | 
|  | sw.Write(">"); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Update xml with hyperlinks | 
|  | /// </summary> | 
|  | /// <param name="sw">The stream</param> | 
|  | private void UpdateHyperLinks(StreamWriter sw) | 
|  | { | 
|  | Dictionary<string, string> hyps = new Dictionary<string, string>(); | 
|  | var cse = new CellsStoreEnumerator<Uri>(_hyperLinks); | 
|  | bool first = true; | 
|  | //foreach (ulong cell in _hyperLinks) | 
|  | while(cse.Next()) | 
|  | { | 
|  | if (first) | 
|  | { | 
|  | sw.Write("<hyperlinks>"); | 
|  | first = false; | 
|  | } | 
|  | //int row, col; | 
|  | var uri = _hyperLinks.GetValue(cse.Row, cse.Column); | 
|  | //ExcelCell cell = _cells[cellId] as ExcelCell; | 
|  | if (uri is ExcelHyperLink && !string.IsNullOrEmpty((uri as ExcelHyperLink).ReferenceAddress)) | 
|  | { | 
|  | ExcelHyperLink hl = uri as ExcelHyperLink; | 
|  | sw.Write("<hyperlink ref=\"{0}\" location=\"{1}\" {2}{3}/>", | 
|  | Cells[cse.Row, cse.Column, cse.Row + hl.RowSpann, cse.Column + hl.ColSpann].Address, | 
|  | ExcelCellBase.GetFullAddress(SecurityElement.Escape(Name), SecurityElement.Escape(hl.ReferenceAddress)), | 
|  | string.IsNullOrEmpty(hl.Display) ? "" : "display=\"" + SecurityElement.Escape(hl.Display) + "\" ", | 
|  | string.IsNullOrEmpty(hl.ToolTip) ? "" : "tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\" "); | 
|  | } | 
|  | else if( uri!=null) | 
|  | { | 
|  | string id; | 
|  | Uri hyp; | 
|  | if (uri is ExcelHyperLink) | 
|  | { | 
|  | hyp = ((ExcelHyperLink)uri).OriginalUri; | 
|  | } | 
|  | else | 
|  | { | 
|  | hyp = uri; | 
|  | } | 
|  | if (hyps.ContainsKey(hyp.OriginalString)) | 
|  | { | 
|  | id = hyps[hyp.OriginalString]; | 
|  | } | 
|  | else | 
|  | { | 
|  | var relationship = Part.CreateRelationship(hyp, Packaging.TargetMode.External, ExcelPackage.schemaHyperlink); | 
|  | if (uri is ExcelHyperLink) | 
|  | { | 
|  | ExcelHyperLink hl = uri as ExcelHyperLink; | 
|  | sw.Write("<hyperlink ref=\"{0}\" {2}{3}r:id=\"{1}\" />", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id, | 
|  | string.IsNullOrEmpty(hl.Display) ? "" : "display=\"" + SecurityElement.Escape(hl.Display) + "\" ", | 
|  | string.IsNullOrEmpty(hl.ToolTip) ? "" : "tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\" "); | 
|  | } | 
|  | else | 
|  | { | 
|  | sw.Write("<hyperlink ref=\"{0}\" r:id=\"{1}\" />", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id); | 
|  | } | 
|  | id = relationship.Id; | 
|  | } | 
|  | //cell.HyperLinkRId = id; | 
|  | } | 
|  | } | 
|  | if (!first) | 
|  | { | 
|  | sw.Write("</hyperlinks>"); | 
|  | } | 
|  | } | 
|  | /// <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 | 
|  | /// </summary> | 
|  | public ExcelAddressBase Dimension | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | int fromRow, fromCol, toRow, toCol; | 
|  | if (_values.GetDimension(out fromRow, out fromCol, out toRow, out toCol)) | 
|  | { | 
|  | var addr = new ExcelAddressBase(fromRow, fromCol, toRow, toCol); | 
|  | addr._ws = Name; | 
|  | return addr; | 
|  | } | 
|  | else | 
|  | { | 
|  | return null; | 
|  | } | 
|  | } | 
|  | } | 
|  | ExcelSheetProtection _protection=null; | 
|  | /// <summary> | 
|  | /// Access to sheet protection properties | 
|  | /// </summary> | 
|  | public ExcelSheetProtection Protection | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_protection == null) | 
|  | { | 
|  | _protection = new ExcelSheetProtection(NameSpaceManager, TopNode, this); | 
|  | } | 
|  | return _protection; | 
|  | } | 
|  | } | 
|  |  | 
|  | private ExcelProtectedRangeCollection _protectedRanges; | 
|  | public ExcelProtectedRangeCollection ProtectedRanges | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_protectedRanges == null) | 
|  | _protectedRanges = new ExcelProtectedRangeCollection(NameSpaceManager, TopNode, this); | 
|  | return _protectedRanges; | 
|  | } | 
|  | } | 
|  |  | 
|  | #region Drawing | 
|  | ExcelDrawings _drawings = null; | 
|  | /// <summary> | 
|  | /// Collection of drawing-objects like shapes, images and charts | 
|  | /// </summary> | 
|  | public ExcelDrawings Drawings | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_drawings == null) | 
|  | { | 
|  | _drawings = new ExcelDrawings(_package, this); | 
|  | } | 
|  | return _drawings; | 
|  | } | 
|  | } | 
|  | #endregion | 
|  | ExcelTableCollection _tables = null; | 
|  | /// <summary> | 
|  | /// Tables defined in the worksheet. | 
|  | /// </summary> | 
|  | public ExcelTableCollection Tables | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (Workbook._nextTableID == int.MinValue) Workbook.ReadAllTables(); | 
|  | if (_tables == null) | 
|  | { | 
|  | _tables = new ExcelTableCollection(this); | 
|  | } | 
|  | return _tables; | 
|  | } | 
|  | } | 
|  | ExcelPivotTableCollection _pivotTables = null; | 
|  | /// <summary> | 
|  | /// Pivottables defined in the worksheet. | 
|  | /// </summary> | 
|  | public ExcelPivotTableCollection PivotTables | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (_pivotTables == null) | 
|  | { | 
|  | if (Workbook._nextPivotTableID == int.MinValue) Workbook.ReadAllTables(); | 
|  | _pivotTables = new ExcelPivotTableCollection(this); | 
|  | } | 
|  | return _pivotTables; | 
|  | } | 
|  | } | 
|  | private ExcelConditionalFormattingCollection _conditionalFormatting = null; | 
|  | /// <summary> | 
|  | /// ConditionalFormatting defined in the worksheet. Use the Add methods to create ConditionalFormatting and add them to the worksheet. Then | 
|  | /// set the properties on the instance returned. | 
|  | /// </summary> | 
|  | /// <seealso cref="ExcelConditionalFormattingCollection"/> | 
|  | public ExcelConditionalFormattingCollection ConditionalFormatting | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (_conditionalFormatting == null) | 
|  | { | 
|  | _conditionalFormatting = new ExcelConditionalFormattingCollection(this); | 
|  | } | 
|  | return _conditionalFormatting; | 
|  | } | 
|  | } | 
|  | private ExcelDataValidationCollection _dataValidation = null; | 
|  | /// <summary> | 
|  | /// DataValidation defined in the worksheet. Use the Add methods to create DataValidations and add them to the worksheet. Then | 
|  | /// set the properties on the instance returned. | 
|  | /// </summary> | 
|  | /// <seealso cref="ExcelDataValidationCollection"/> | 
|  | public ExcelDataValidationCollection DataValidations | 
|  | { | 
|  | get | 
|  | { | 
|  | CheckSheetType(); | 
|  | if (_dataValidation == null) | 
|  | { | 
|  | _dataValidation = new ExcelDataValidationCollection(this); | 
|  | } | 
|  | return _dataValidation; | 
|  | } | 
|  | } | 
|  | ExcelBackgroundImage _backgroundImage = null; | 
|  | /// <summary> | 
|  | /// An image displayed as the background of the worksheet. | 
|  | /// </summary> | 
|  | public ExcelBackgroundImage BackgroundImage | 
|  | { | 
|  | get | 
|  | { | 
|  | if (_backgroundImage == null) | 
|  | { | 
|  | _backgroundImage = new ExcelBackgroundImage(NameSpaceManager, TopNode, this); | 
|  | } | 
|  | return _backgroundImage; | 
|  | } | 
|  | } | 
|  | /// <summary> | 
|  | /// Returns the style ID given a style name. | 
|  | /// The style ID will be created if not found, but only if the style name exists! | 
|  | /// </summary> | 
|  | /// <param name="StyleName"></param> | 
|  | /// <returns></returns> | 
|  | internal int GetStyleID(string StyleName) | 
|  | { | 
|  | ExcelNamedStyleXml namedStyle=null; | 
|  | Workbook.Styles.NamedStyles.FindByID(StyleName, ref namedStyle); | 
|  | if (namedStyle.XfId == int.MinValue) | 
|  | { | 
|  | namedStyle.XfId=Workbook.Styles.CellXfs.FindIndexByID(namedStyle.Style.Id); | 
|  | } | 
|  | return namedStyle.XfId; | 
|  | } | 
|  | /// <summary> | 
|  | /// The workbook object | 
|  | /// </summary> | 
|  | public ExcelWorkbook Workbook | 
|  | { | 
|  | get | 
|  | { | 
|  | return _package.Workbook; | 
|  | } | 
|  | } | 
|  | #endregion | 
|  | #endregion  // END Worksheet Private Methods | 
|  |  | 
|  | /// <summary> | 
|  | /// Get the next ID from a shared formula or an Array formula | 
|  | /// Sharedforumlas will have an id from 0-x. Array formula ids start from 0x4000001-. | 
|  | /// </summary> | 
|  | /// <param name="isArray">If the formula is an array formula</param> | 
|  | /// <returns></returns> | 
|  | internal int GetMaxShareFunctionIndex(bool isArray) | 
|  | { | 
|  | int i=_sharedFormulas.Count + 1; | 
|  | if (isArray) | 
|  | i |= 0x40000000; | 
|  |  | 
|  | while(_sharedFormulas.ContainsKey(i)) | 
|  | { | 
|  | i++; | 
|  | } | 
|  | 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; | 
|  | while(cse.MoveNext()) | 
|  | { | 
|  | if (cse.Value is DateTime) | 
|  | { | 
|  | try | 
|  | { | 
|  | double sdv = ((DateTime)cse.Value).ToOADate(); | 
|  | sdv += offset; | 
|  |  | 
|  | cse.Value = DateTime.FromOADate(sdv); | 
|  | } | 
|  | catch | 
|  | { | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | internal string GetFormula(int row, int col) | 
|  | { | 
|  | var v = _formulas.GetValue(row, col); | 
|  | if (v is int) | 
|  | { | 
|  | return _sharedFormulas[(int)v].GetFormula(row,col, Name); | 
|  | } | 
|  | else if (v != null) | 
|  | { | 
|  | return v.ToString(); | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  | internal string GetFormulaR1C1(int row, int col) | 
|  | { | 
|  | var v = _formulas.GetValue(row, col); | 
|  | if (v is int) | 
|  | { | 
|  | var sf = _sharedFormulas[(int)v]; | 
|  | return ExcelCellBase.TranslateToR1C1(Formulas.RemoveDummyFunction(sf.Formula), sf.StartRow, sf.StartCol); | 
|  | } | 
|  | else if (v != null) | 
|  | { | 
|  | return ExcelCellBase.TranslateToR1C1(v.ToString(), row, col); | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | internal string GetFormulaR1C1_V1(int row, int col) | 
|  | { | 
|  | var v = _formulas.GetValue(row, col); | 
|  | if (v is int) | 
|  | { | 
|  | var sf = _sharedFormulas[(int)v]; | 
|  | return ExcelCellBase.TranslateToR1C1_V1(Formulas.RemoveDummyFunction(sf.Formula), sf.StartRow, sf.StartCol); | 
|  | } | 
|  | else if (v != null) | 
|  | { | 
|  | return ExcelCellBase.TranslateToR1C1_V1(v.ToString(), row, col); | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | internal string GetArrayFormulaAddress(int row, int col) | 
|  | { | 
|  | var v = _formulas.GetValue(row, col); | 
|  | if ((v is int) && (_sharedFormulas[(int)v].IsArray)) | 
|  | { | 
|  | return _sharedFormulas[(int)v].Address; | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | private void DisposeInternal(IDisposable candidateDisposable) | 
|  | { | 
|  | if (candidateDisposable != null) | 
|  | { | 
|  | candidateDisposable.Dispose(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | public void Dispose() | 
|  | { | 
|  | DisposeInternal(_values); | 
|  | DisposeInternal(_formulas); | 
|  | DisposeInternal(_flags); | 
|  | DisposeInternal(_hyperLinks); | 
|  | DisposeInternal(_styles); | 
|  | DisposeInternal(_types); | 
|  | DisposeInternal(_commentsStore); | 
|  | DisposeInternal(_formulaTokens); | 
|  |  | 
|  | _values = null; | 
|  | _formulas = null; | 
|  | _flags = null; | 
|  | _hyperLinks = null; | 
|  | _styles = null; | 
|  | _types = null; | 
|  | _commentsStore = null; | 
|  | _formulaTokens = null; | 
|  |  | 
|  | _package = null; | 
|  | _pivotTables = null; | 
|  | _protection = null; | 
|  | if(_sharedFormulas != null) _sharedFormulas.Clear(); | 
|  | _sharedFormulas = null; | 
|  | _sheetView = null; | 
|  | _tables = null; | 
|  | _vmlDrawings = null; | 
|  | _conditionalFormatting = null; | 
|  | _dataValidation = null; | 
|  | _drawings = null; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Get the ExcelColumn for column (span ColumnMin and ColumnMax) | 
|  | /// </summary> | 
|  | /// <param name="column"></param> | 
|  | /// <returns></returns> | 
|  | internal ExcelColumn GetColumn(int column) | 
|  | { | 
|  | var c = _values.GetValue(0, column) as ExcelColumn; | 
|  | if (c == null) | 
|  | { | 
|  | int row = 0, col = column; | 
|  | if (_values.PrevCell(ref row, ref col)) | 
|  | { | 
|  | c = _values.GetValue(0, col) as ExcelColumn; | 
|  | if (c != null && c.ColumnMax >= column) | 
|  | { | 
|  | return c; | 
|  | } | 
|  | return null; | 
|  | } | 
|  | } | 
|  | return c; | 
|  |  | 
|  | } | 
|  |  | 
|  | public bool Equals(ExcelWorksheet x, ExcelWorksheet y) | 
|  | { | 
|  | return x.Name == y.Name && x.SheetID == y.SheetID && x.WorksheetXml.OuterXml == y.WorksheetXml.OuterXml; | 
|  | } | 
|  |  | 
|  | public int GetHashCode(ExcelWorksheet obj) | 
|  | { | 
|  | return obj.WorksheetXml.OuterXml.GetHashCode(); | 
|  | } | 
|  | }  // END class Worksheet | 
|  | } |