/******************************************************************************* * 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.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 { /// /// Worksheet hidden enumeration /// public enum eWorkSheetHidden { /// /// The worksheet is visible /// Visible, /// /// The worksheet is hidden but can be shown by the user via the user interface /// Hidden, /// /// The worksheet is hidden and cannot be shown by the user via the user interface /// VeryHidden } [Flags] internal enum CellFlags { //Merged = 0x1, RichText = 0x2, SharedFormula = 0x4, ArrayFormula = 0x8 } /// /// Represents an Excel Chartsheet and provides access to its properties and methods /// 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]; } } } /// /// Represents an Excel worksheet and provides access to its properties and methods /// public class ExcelWorksheet : XmlHelper, IDisposable { internal class Formulas { public Formulas(ISourceCodeTokenizer tokenizer) { _tokenizer = tokenizer; } 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 Tokens {get; set;} internal string GetFormula(int row, int column, string worksheet) { if (StartRow == row && StartCol == column) { return Formula; } if (Tokens == null) { Tokens = _tokenizer.Tokenize(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; } } /// /// Collection containing merged cell addresses /// public class MergeCellsCollection : IEnumerable { internal MergeCellsCollection() { } internal CellStore _cells = new CellStore(); List _list = new List(); internal List 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(_cells, address._fromRow, address._fromCol, address._toRow, address._toCol); //cells while(cse.Next()) { return false; } //Entire column cse = new CellsStoreEnumerator(_cells, 0, address._fromCol, 0, address._toCol); while (cse.Next()) { return false; } //Entire row cse = new CellsStoreEnumerator(_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 Members public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _list.GetEnumerator(); } #endregion internal void Delete(ExcelAddressBase Destination) { var cse = new CellsStoreEnumerator(_cells, Destination._fromRow, Destination._fromCol, Destination._toRow, Destination._toCol); var used=new HashSet(); 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.Delete(Destination._fromRow, Destination._fromCol, Destination._toRow - Destination._fromRow + 1, Destination._toCol - Destination._fromCol + 1); foreach(var i in used) { _list[i] = null; } } } internal CellStore _values; internal CellStore _types; internal CellStore _styles; internal CellStore _formulas; internal FlagCellStore _flags; internal CellStore> _formulaTokens; internal CellStore _hyperLinks; internal CellStore _commentsStore; internal Dictionary _sharedFormulas = new Dictionary(); 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 /// /// A worksheet /// /// Namespacemanager /// Package /// Relationship ID /// URI /// Name of the sheet /// Sheet id /// Position /// hide 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(); _types = new CellStore(); _styles = new CellStore(); _formulas = new CellStore(); _flags = new FlagCellStore(); _commentsStore = new CellStore(); _hyperLinks = new CellStore(); _names = new ExcelNamedRangeCollection(Workbook,this); CreateXml(); TopNode = _worksheetXml.DocumentElement; } #endregion /// /// The Uri to the worksheet within the package /// internal Uri WorksheetUri { get { return (_worksheetUri); } } /// /// The Zip.ZipPackagePart for the worksheet within the package /// internal Packaging.ZipPackagePart Part { get { return (_package.Package.GetPart(WorksheetUri)); } } /// /// The ID for the worksheet's relationship with the workbook in the package /// internal string RelationshipID { get { return (_relationshipID); } } /// /// The unique identifier for the worksheet. /// internal int SheetID { get { return (_sheetID); } } /// /// The position of the worksheet. /// internal int PositionID { get { return (_positionID); } set { _positionID = value; } } #region Worksheet Public Properties /// /// The index in the worksheets collection /// public int Index { get { return (_positionID); } } /// /// Address for autofilter /// /// 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")); } } /// /// Returns a ExcelWorksheetView object that allows you to set the view state properties of the worksheet /// 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); } } /// /// The worksheet's display name as it appears on the tab /// 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; /// /// Provides access to named ranges /// public ExcelNamedRangeCollection Names { get { CheckSheetType(); return _names; } } /// /// Indicates if the worksheet is hidden in the workbook /// 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; /// /// Get/set the default height of all rows in the worksheet /// 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; } } } /// /// Get/set the default width of all rows in the worksheet /// 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; } } } /** **/ const string outLineSummaryBelowPath = "d:sheetPr/d:outlinePr/@summaryBelow"; /// /// Summary rows below details /// public bool OutLineSummaryBelow { get { CheckSheetType(); return GetXmlNodeBool(outLineSummaryBelowPath); } set { CheckSheetType(); SetXmlNodeString(outLineSummaryBelowPath, value ? "1" : "0"); } } const string outLineSummaryRightPath = "d:sheetPr/d:outlinePr/@summaryRight"; /// /// Summary rows to right of details /// public bool OutLineSummaryRight { get { CheckSheetType(); return GetXmlNodeBool(outLineSummaryRightPath); } set { CheckSheetType(); SetXmlNodeString(outLineSummaryRightPath, value ? "1" : "0"); } } const string outLineApplyStylePath = "d:sheetPr/d:outlinePr/@applyStyles"; /// /// Automatic styles /// public bool OutLineApplyStyle { get { CheckSheetType(); return GetXmlNodeBool(outLineApplyStylePath); } set { CheckSheetType(); SetXmlNodeString(outLineApplyStylePath, value ? "1" : "0"); } } const string tabColorPath = "d:sheetPr/d:tabColor/@rgb"; /// /// Color of the sheet tab /// 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 /// /// 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. /// public XmlDocument WorksheetXml { get { return (_worksheetXml); } } internal ExcelVmlDrawingCommentCollection _vmlDrawings = null; /// /// Vml drawings. underlaying object for comments /// internal ExcelVmlDrawingCommentCollection VmlDrawingsComments { get { if (_vmlDrawings == null) { CreateVmlCollection(); } return _vmlDrawings; } } internal ExcelCommentCollection _comments = null; /// /// Collection of comments /// 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(); } /// /// Get the lenth of the attributes /// Conditional formatting attributes can be extremly long som get length of the attributes to finetune position. /// /// /// 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 += "" + 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(); 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); } } } } } /// /// Read until the node is found. If not found the xmlreader is reseted. /// /// The reader /// Text to search for /// Alternative text to search for /// 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; } /// /// Load Hyperlinks /// /// The reader 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; if (uri.IsAbsoluteUri) { try { hl = new ExcelHyperLink(uri.AbsoluteUri); } catch { hl = new ExcelHyperLink(uri.OriginalString, UriKind.Absolute); } } else { hl = new ExcelHyperLink(uri.OriginalString, 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; } } } /// /// Load cells /// /// The reader private void LoadCells(XmlTextReader xr) { //var cellList=new List(); //var rowList = new List(); //var formulaList = new List(); 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 = 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"); string formula = xr.ReadElementContentAsString(); var afIndex = GetMaxShareFunctionIndex(true); _formulas.SetValue(address._fromRow, address._fromCol, afIndex); _values.SetValue(address._fromRow, address._fromCol, 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, xr.ReadInnerXml()); //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; } /// /// Load merged cells /// /// 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); } } } } /// /// Update merged cells /// /// The writer private void UpdateMergedCells(StreamWriter sw) { sw.Write(""); foreach (string address in _mergedCells) { sw.Write("", address); } sw.Write(""); } /// /// Reads a row from the XML reader /// /// The reader /// The row number /// 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 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(); _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 >= 14 && nf <= 22) || (nf >= 45 && nf <= 47)) { 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 { _values.SetValue(row, col, ""); } } else { _values.SetValue(row, col, ""); } } else { double d; if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) { _values.SetValue(row, col, d); } else { _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 /// /// 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 /// /// /// To format the text you can use the following format /// /// PrefixDescription /// &UUnderlined /// &EDouble Underline /// &K:xxxxxxColor. ex &K:FF0000 for red /// &"Font,Regular Bold Italic"Changes the font. Regular or Bold or Italic or Bold Italic can be used. ex &"Arial,Bold Italic" /// &nnChange font size. nn is an integer. ex &24 /// &GPlaceholder for images. Images can not be added by the library, but its possible to use in a template. /// /// 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" /// /// Printer settings /// 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 ///// ///// Provides access to an individual cell within the worksheet. ///// ///// The row number in the worksheet ///// The column number in the worksheet ///// //internal ExcelCell Cell(int row, int col) //{ // return new ExcelCell(_values, row, col); //} /// /// Provides access to a range of cells /// public ExcelRange Cells { get { CheckSheetType(); return new ExcelRange(this, 1, 1, ExcelPackage.MaxRows, ExcelPackage.MaxColumns); } } /// /// Provides access to the selected range of cells /// public ExcelRange SelectedRange { get { CheckSheetType(); return new ExcelRange(this, View.SelectedRange); } } MergeCellsCollection _mergedCells = new MergeCellsCollection(); /// /// Addresses to merged ranges /// public MergeCellsCollection MergedCells { get { CheckSheetType(); return _mergedCells; } } /// /// Provides access to an individual row within the worksheet so you can set its properties. /// /// The row number in the worksheet /// 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; } /// /// Provides access to an individual column within the worksheet so you can set its properties. /// /// The column number in the worksheet /// 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; } /// /// Returns the name of the worksheet /// /// The name of the worksheet 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._hidden = c.Hidden; newC.OutlineLevel = c.OutlineLevel; newC.Phonetic = c.Phonetic; newC.BestFit = c.BestFit; //_columns.Add(newC); _values.SetValue(0, col, newC); newC.Width = c._width; return newC; } /// /// Make the current worksheet active. /// public void Select() { View.TabSelected = true; //Select(Address, true); } /// /// Selects a range in the worksheet. The active cell is the topmost cell. /// Make the current worksheet active. /// /// An address range public void Select(string Address) { Select(Address, true); } /// /// Selects a range in the worksheet. The actice cell is the topmost cell. /// /// A range of cells /// Make the sheet active 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); } /// /// Selects a range in the worksheet. The active cell is the topmost cell of the first address. /// Make the current worksheet active. /// /// An address range public void Select(ExcelAddress Address) { CheckSheetType(); Select(Address, true); } /// /// Selects a range in the worksheet. The active cell is the topmost cell of the first address. /// /// A range of cells /// Make the sheet active 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 /// /// 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. /// /// The position of the new row /// Number of rows to insert public void InsertRow(int rowFrom, int rows) { InsertRow(rowFrom, rows, 0); } /// /// 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. /// /// The position of the new row /// Number of rows to insert. /// Copy Styles from this row. Applied to all inserted rows 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(_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(_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); } } } /// /// 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. /// /// The position of the new column /// Number of columns to insert public void InsertColumn(int columnFrom, int columns) { InsertColumn(columnFrom, columns, 0); } /// /// 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. /// /// The position of the new column /// Number of columns to insert. /// Copy Styles from this column. Applied to all inserted columns 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(_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(_values, 0, 1, 0, ExcelPackage.MaxColumns); var lst = new List(); 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); } /// /// Adds a value to the row of merged cells to fix for inserts or deletes /// /// /// /// private void FixMergedCellsRow(int row, int rows, bool delete) { List removeIndex = new List(); 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); _mergedCells._cells.Delete(row, 0, rows, 0); 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]); } } /// /// Adds a value to the row of merged cells to fix for inserts or deletes /// /// /// /// private void FixMergedCellsColumn(int column, int columns, bool delete) { List removeIndex = new List(); 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); _mergedCells._cells.Delete(0, column, 0, columns); if (newAddr == null) { removeIndex.Add(i); continue; } } else { newAddr = addr.AddColumn(column, columns); if (newAddr.Address != addr.Address) { _mergedCells._cells.Insert(0, column, 0, columns); _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 added = new List(); List deleted = new List(); 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(); 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 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 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 /// /// Delete the specified row from the worksheet. /// /// A row to be deleted public void DeleteRow(int row) { DeleteRow(row, 1); } /// /// Delete the specified row from the worksheet. /// /// The start row /// Number of rows to delete 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, 1, rows, ExcelPackage.MaxColumns); _types.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); _formulas.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); _styles.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); _flags.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); _commentsStore.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); _hyperLinks.Delete(rowFrom, 1, rows, ExcelPackage.MaxColumns); AdjustFormulasRow(rowFrom, rows); FixMergedCellsRow(rowFrom, rows, true); foreach (var tbl in Tables) { tbl.Address = tbl.Address.DeleteRow(rowFrom, rows); } } } /// /// Delete the specified column from the worksheet. /// /// The column to be deleted public void DeleteColumn(int column) { DeleteColumn(column,1); } /// /// Delete the specified column from the worksheet. /// /// The start column /// Number of columns to delete 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(1, columnFrom, ExcelPackage.MaxRows, columns); _types.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); _formulas.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); _styles.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); _flags.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); _commentsStore.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); _hyperLinks.Delete(1, columnFrom, ExcelPackage.MaxRows, columns); AdjustFormulasColumn(columnFrom, columns); FixMergedCellsColumn(columnFrom, columns, true); var csec = new CellsStoreEnumerator(_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(); 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(_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(); 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(_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); } } } /// /// Deletes the specified row from the worksheet. /// /// The number of the start row to be deleted /// Number of rows to delete /// Not used. Rows are always shifted public void DeleteRow(int rowFrom, int rows, bool shiftOtherRowsUp) { DeleteRow(rowFrom, rows); } #endregion /// /// Get the cell value from thw worksheet /// /// The row number /// The row number /// The value 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; } } /// /// Get a strongly typed cell value from the worksheet /// /// The type /// The row number /// The row number /// The value. If the value can't be converted to the specified type, the default value will be returned public T GetValue(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(v); } } //Thanks to Michael Tran for parts of this method internal T GetTypedValue(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() 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); } } } } /// /// Set the value of a cell /// /// The row number /// The column number /// The value 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); } /// /// Set the value of a cell /// /// The Excel address /// The value 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 /// /// Get MergeCell Index No /// /// /// /// 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); } ///// ///// Saves the worksheet to the package. ///// //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)); // } // } // } // } //} /// /// Delete the printersettings relationship and part. /// 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()); } } } /// /// Save all table data /// private void SaveTables() { foreach (var tbl in Tables) { if (tbl.ShowHeader || tbl.ShowTotal) { int colNum = tbl.Address._fromCol; var colVal = new HashSet(); foreach (var col in tbl.Columns) { var 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); 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); } } pt.PivotTableXml.Save(pt.Part.GetStream(FileMode.Create)); pt.CacheDefinition.CacheDefinitionXml.Save(pt.CacheDefinition.Part.GetStream(FileMode.Create)); } } 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(); //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(); 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(_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("", cse.Column); count++; } } if (count > 0) { sw.Write(string.Format("{1}", count, breaks.ToString())); } } private void UpdateRowBreaks(StreamWriter sw) { StringBuilder breaks=new StringBuilder(); int count = 0; var cse = new CellsStoreEnumerator(_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("", cse.Row); count++; } } if (count>0) { sw.Write(string.Format("{1}", count, breaks.ToString())); } } /// /// Inserts the cols collection into the XML document /// 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(_values, 0, 1, 0, ExcelPackage.MaxColumns); //sw.Write(""); //foreach (ExcelColumn col in _columns) bool first = true; while(cse.Next()) { if (first) { sw.Write(""); first = false; } var col = cse.Value as ExcelColumn; ExcelStyleCollection cellXfs = _package.Workbook.Styles.CellXfs; sw.Write(" 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(""); } } /// /// Insert row and cells into the XML document /// private void UpdateRowCellData(StreamWriter sw) { ExcelStyleCollection 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(""); //Set a value for cells with style and no value set. var cseStyle = new CellsStoreEnumerator(_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(_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("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, SecurityElement.Escape(f.Formula), GetFormulaValue(v), GetCellType(v,true)); } else { cache.AppendFormat("{4}{5}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, SecurityElement.Escape(f.Formula), GetFormulaValue(v), GetCellType(v,true)); } } else if (f.IsArray) { cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); } else { cache.AppendFormat("{3}", 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("{3}{4}", 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("", f.Address, styleID < 0 ? 0 : styleID); cache.AppendFormat("{0}{1}", SecurityElement.Escape(f.Formula), GetFormulaValue(v)); } } } else if (formula!=null && formula.ToString()!="") { cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v,true)); cache.AppendFormat("{0}{1}", SecurityElement.Escape(formula.ToString()), GetFormulaValue(v)); } else { if (v == null && styleID > 0) { cache.AppendFormat("", 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("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v)); cache.AppendFormat("{0}", 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("", cse.CellAddress, styleID < 0 ? 0 : styleID); cache.AppendFormat("{0}", 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(""); cache.Append(""); sw.Write(cache.ToString()); sw.Flush(); } private object GetFormulaValue(object v) { //if (_package.Workbook._isCalculated) //{ if (v != null && v.ToString()!="") { return "" + SecurityElement.Escape(GetValueForXml(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 cellXfs, int prevRow, int row) { if (prevRow != -1) cache.Append(""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); cache.AppendFormat(""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); sw.Write("