Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Async/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/3.1.3/EPPlus-3.1.3/ExcelWorksheet.cs @ 12214

Last change on this file since 12214 was 9580, checked in by sforsten, 12 years ago

#1730:

  • added SymbolicDataAnalysisExpressionExcelFormatter
  • changed modifiers in SymbolicExpressionTreeChart of methods SaveImageAsBitmap and SaveImageAsEmf to public
  • added menu item ExportSymbolicSolutionToExcelMenuItem to export a symbolic solution to an excel file
  • added EPPlus-3.1.3 to ExtLibs
File size: 115.1 KB
Line 
1/*******************************************************************************
2 * You may amend and distribute as you like, but don't remove this header!
3 *
4 * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
5 * See http://www.codeplex.com/EPPlus for details.
6 *
7 * Copyright (C) 2011  Jan Källman
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
17 * See the GNU Lesser General Public License for more details.
18 *
19 * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
20 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
21 *
22 * All code and executables are provided "as is" with no warranty either express or implied.
23 * The author accepts no liability for any damage or loss of business that this product may cause.
24 *
25 * Code change notes:
26 *
27 * Author             Change            Date
28 * ******************************************************************************
29 * Jan Källman        Initial Release           2011-11-02
30 * Jan Källman          Total rewrite               2010-03-01
31 * Jan Källman        License changed GPL-->LGPL  2011-12-27
32 *******************************************************************************/
33using System;
34using System.Xml;
35using System.Collections.Generic;
36using System.IO;
37using System.IO.Packaging;
38using System.Configuration;
39using OfficeOpenXml.Drawing;
40using System.Diagnostics;
41using OfficeOpenXml.Style;
42using System.Globalization;
43using System.Text;
44using System.Security;
45using OfficeOpenXml.Drawing.Chart;
46using OfficeOpenXml.Style.XmlAccess;
47using System.Text.RegularExpressions;
48using OfficeOpenXml.Drawing.Vml;
49using OfficeOpenXml.Table;
50using OfficeOpenXml.DataValidation;
51using OfficeOpenXml.Table.PivotTable;
52using System.ComponentModel;
53using System.Drawing;
54using OfficeOpenXml.ConditionalFormatting;
55using System.Web;
56namespace OfficeOpenXml
57{
58    /// <summary>
59    /// Worksheet hidden enumeration
60    /// </summary>
61    public enum eWorkSheetHidden
62    {
63        /// <summary>
64        /// The worksheet is visible
65        /// </summary>
66        Visible,
67        /// <summary>
68        /// The worksheet is hidden but can be shown by the user via the user interface
69        /// </summary>
70        Hidden,
71        /// <summary>
72        /// The worksheet is hidden and cannot be shown by the user via the user interface
73        /// </summary>
74        VeryHidden
75    }
76    /// <summary>
77  /// Represents an Excel worksheet and provides access to its properties and methods
78  /// </summary>
79    public sealed class ExcelWorksheet : XmlHelper
80  {
81        internal class Formulas
82        {
83            internal int Index { get; set; }
84            internal string Address { get; set; }
85            internal bool IsArray { get; set; }
86            public string Formula { get; set; }
87            public int StartRow { get; set; }
88            public int StartCol { get; set; }
89        }
90        /// <summary>
91        /// Collection containing merged cell addresses
92        /// </summary>
93        /// <typeparam name="T"></typeparam>
94        public class MergeCellsCollection<T> : IEnumerable<T>
95        {
96            private List<T> _list = new List<T>();
97            internal MergeCellsCollection()
98            {
99
100            }
101            internal List<T> List { get {return _list;} }
102            public T this[int Index]
103            {
104                get
105                {
106                    return _list[Index];
107                }
108            }
109            public int Count
110            {
111                get
112                {
113                    return _list.Count;
114                }
115            }
116            internal void Remove(T Item)
117            {
118                _list.Remove(Item);
119            }
120            #region IEnumerable<T> Members
121
122            public IEnumerator<T> GetEnumerator()
123            {
124                return _list.GetEnumerator();
125            }
126
127            #endregion
128
129            #region IEnumerable Members
130
131            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
132            {
133                return _list.GetEnumerator();
134            }
135
136            #endregion
137        }
138        internal RangeCollection _cells;
139        internal RangeCollection _columns;
140        internal RangeCollection _rows;
141
142        internal Dictionary<int, Formulas> _sharedFormulas = new Dictionary<int, Formulas>();
143        internal Dictionary<int, Formulas> _arrayFormulas = new Dictionary<int, Formulas>();
144        internal RangeCollection _formulaCells;
145        internal int _minCol = ExcelPackage.MaxColumns;
146        internal int _maxCol = 0;
147        internal List<ulong> _hyperLinkCells;   //Used when saving the sheet
148        #region Worksheet Private Properties
149        internal ExcelPackage _package;
150    private Uri _worksheetUri;
151    private string _name;
152    private int _sheetID;
153        private int _positionID;
154    private string _relationshipID;
155    private XmlDocument _worksheetXml;
156        internal ExcelWorksheetView _sheetView;
157    internal ExcelHeaderFooter _headerFooter;
158        #endregion
159        #region ExcelWorksheet Constructor
160        /// <summary>
161        /// A worksheet
162        /// </summary>
163        /// <param name="ns">Namespacemanager</param>
164        /// <param name="excelPackage">Package</param>
165        /// <param name="relID">Relationship ID</param>
166        /// <param name="uriWorksheet">URI</param>
167        /// <param name="sheetName">Name of the sheet</param>
168        /// <param name="sheetID">Sheet id</param>
169        /// <param name="positionID">Position</param>
170        /// <param name="hide">hide</param>
171        public ExcelWorksheet(XmlNamespaceManager ns, ExcelPackage excelPackage, string relID,
172                              Uri uriWorksheet, string sheetName, int sheetID, int positionID,
173                              eWorkSheetHidden hide) :
174            base(ns, null)
175        {
176            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" };
177            _package = excelPackage;   
178            _relationshipID = relID;
179            _worksheetUri = uriWorksheet;
180            _name = sheetName;
181            _sheetID = sheetID;
182            _positionID = positionID;
183            Hidden = hide;
184            _names = new ExcelNamedRangeCollection(Workbook,this);
185            CreateXml();
186            TopNode = _worksheetXml.DocumentElement;
187        }
188
189        #endregion
190        /// <summary>
191        /// The Uri to the worksheet within the package
192        /// </summary>
193        internal Uri WorksheetUri { get { return (_worksheetUri); } }
194        /// <summary>
195        /// The PackagePart for the worksheet within the package
196        /// </summary>
197        internal PackagePart Part { get { return (_package.Package.GetPart(WorksheetUri)); } }
198        /// <summary>
199        /// The ID for the worksheet's relationship with the workbook in the package
200        /// </summary>
201        internal string RelationshipID { get { return (_relationshipID); } }
202        /// <summary>
203        /// The unique identifier for the worksheet.
204        /// </summary>
205        internal int SheetID { get { return (_sheetID); } }
206        /// <summary>
207        /// The position of the worksheet.
208        /// </summary>
209        internal int PositionID { get { return (_positionID); } set { _positionID = value; } }
210    #region Worksheet Public Properties
211      /// <summary>
212        /// The index in the worksheets collection
213        /// </summary>
214        public int Index { get { return (_positionID); } }
215        /// <summary>
216        /// Address for autofilter
217        /// <seealso cref="ExcelRangeBase.AutoFilter" />       
218        /// </summary>
219        public ExcelAddressBase AutoFilterAddress
220        {
221            get
222            {
223                string address = GetXmlNodeString("d:autoFilter/@ref");
224                if (address == "")
225                {
226                    return null;
227                }
228                else
229                {
230                    return new ExcelAddressBase(address);
231                }
232            }
233            internal set
234            {
235                SetXmlNodeString("d:autoFilter/@ref", value.Address);
236            }
237        }
238
239    /// <summary>
240    /// Returns a ExcelWorksheetView object that allows you to set the view state properties of the worksheet
241    /// </summary>
242    public ExcelWorksheetView View
243    {
244      get
245      {
246        if (_sheetView == null)
247        {
248                    XmlNode node = TopNode.SelectSingleNode("d:sheetViews/d:sheetView", NameSpaceManager);
249                    if (node == null)
250                    {
251                        CreateNode("d:sheetViews/d:sheetView"); //this one shouls always exist. but check anyway
252                        node = TopNode.SelectSingleNode("d:sheetViews/d:sheetView", NameSpaceManager);
253                    }
254                    _sheetView = new ExcelWorksheetView(NameSpaceManager, node, this);
255        }
256        return (_sheetView);
257      }
258    }
259
260    /// <summary>
261    /// The worksheet's display name as it appears on the tab
262    /// </summary>
263    public string Name
264    {
265      get { return (_name); }
266      set
267      {
268                if (value == _name) return;
269                Name=_package.Workbook.Worksheets.ValidateFixSheetName(Name);
270                _package.Workbook.SetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@name", _sheetID), value);
271        _name = value;
272            }
273    }
274        internal ExcelNamedRangeCollection _names;
275        /// <summary>
276        /// Provides access to named ranges
277        /// </summary>
278        public ExcelNamedRangeCollection Names
279        {
280            get
281            {
282                return _names;
283            }
284        }
285    /// <summary>
286    /// Indicates if the worksheet is hidden in the workbook
287    /// </summary>
288    public eWorkSheetHidden Hidden
289    {
290      get
291            {
292                string state=_package.Workbook.GetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID));
293                if (state == "hidden")
294                {
295                    return eWorkSheetHidden.Hidden;
296                }
297                else if (state == "veryHidden")
298                {
299                    return eWorkSheetHidden.VeryHidden;
300                }
301                return eWorkSheetHidden.Visible;
302            }
303      set
304      {
305                    if (value == eWorkSheetHidden.Visible)
306                    {
307                        _package.Workbook.DeleteNode(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID));
308                    }
309                    else
310                    {
311                        string v;
312                        v=value.ToString();                       
313                        v=v.Substring(0,1).ToLower()+v.Substring(1);
314                        _package.Workbook.SetXmlNodeString(string.Format("d:sheets/d:sheet[@sheetId={0}]/@state", _sheetID),v );
315                    }
316        }
317    }
318        double _defaultRowHeight = double.NaN;
319        /// <summary>
320    /// Get/set the default height of all rows in the worksheet
321    /// </summary>
322        public double DefaultRowHeight
323    {
324      get
325      {
326        if(double.IsNaN(_defaultRowHeight))
327                {
328                    _defaultRowHeight = GetXmlNodeDouble("d:sheetFormatPr/@defaultRowHeight");
329                    if(double.IsNaN(_defaultRowHeight))
330                    {
331                        _defaultRowHeight = 15; // Excel default height
332                    }
333                }
334        return _defaultRowHeight;
335      }
336      set
337      {
338                _defaultRowHeight = value;
339                SetXmlNodeString("d:sheetFormatPr/@defaultRowHeight", value.ToString(CultureInfo.InvariantCulture));
340                SetXmlNodeBool("d:sheetFormatPr/@customHeight", value != 15);
341
342                if (double.IsNaN(GetXmlNodeDouble("d:sheetFormatPr/@defaultColWidth")))
343                {
344                    DefaultColWidth = 9.140625;
345                }
346      }
347    }
348        /// <summary>
349        /// Get/set the default width of all rows in the worksheet
350        /// </summary>
351        public double DefaultColWidth
352        {
353            get
354            {
355                double ret = GetXmlNodeDouble("d:sheetFormatPr/@defaultColWidth");
356                if (double.IsNaN(ret))
357                {
358                    ret = 9.140625; // Excel's default width
359                }
360                return ret;
361            }
362            set
363            {
364                SetXmlNodeString("d:sheetFormatPr/@defaultColWidth", value.ToString(CultureInfo.InvariantCulture));
365
366                if (double.IsNaN(GetXmlNodeDouble("d:sheetFormatPr/@defaultRowHeight")))
367                {
368                    DefaultRowHeight = 15;
369                }
370            }
371        }
372        /** <outlinePr applyStyles="1" summaryBelow="0" summaryRight="0" /> **/
373        const string outLineSummaryBelowPath = "d:sheetPr/d:outlinePr/@summaryBelow";
374        /// <summary>
375        /// Summary rows below details
376        /// </summary>
377        public bool OutLineSummaryBelow
378        {
379            get
380            {
381                return GetXmlNodeBool(outLineSummaryBelowPath);
382            }
383            set
384            {
385                SetXmlNodeString(outLineSummaryBelowPath, value ? "1" : "0");
386            }
387        }
388        const string outLineSummaryRightPath = "d:sheetPr/d:outlinePr/@summaryRight";
389        /// <summary>
390        /// Summary rows to right of details
391        /// </summary>
392        public bool OutLineSummaryRight
393        {
394            get
395            {
396                return GetXmlNodeBool(outLineSummaryRightPath);
397            }
398            set
399            {
400                SetXmlNodeString(outLineSummaryRightPath, value ? "1" : "0");
401            }
402        }
403        const string outLineApplyStylePath = "d:sheetPr/d:outlinePr/@applyStyles";
404        /// <summary>
405        /// Automatic styles
406        /// </summary>
407        public bool OutLineApplyStyle
408        {
409            get
410            {
411                return GetXmlNodeBool(outLineApplyStylePath);
412            }
413            set
414            {
415                SetXmlNodeString(outLineApplyStylePath, value ? "1" : "0");
416            }
417        }
418        const string tabColorPath = "d:sheetPr/d:tabColor/@rgb";
419        /// <summary>
420        /// Color of the sheet tab
421        /// </summary>
422        public Color TabColor
423        {
424            get
425            {
426                string col = GetXmlNodeString(tabColorPath);
427                if (col == "")
428                {
429                    return Color.Empty;
430                }
431                else
432                {
433                    return Color.FromArgb(int.Parse(col, System.Globalization.NumberStyles.AllowHexSpecifier));
434                }
435            }
436            set
437            {
438                SetXmlNodeString(tabColorPath, value.ToArgb().ToString("X"));
439            }
440        }
441        const string codeModuleNamePath = "d:sheetPr/@codeName";
442        internal string CodeModuleName
443        {
444            get
445            {
446                return GetXmlNodeString(codeModuleNamePath);
447            }
448            set
449            {
450                SetXmlNodeString(codeModuleNamePath, value);
451            }
452        }
453        internal void CodeNameChange(string value)
454        {
455            CodeModuleName = value;
456        }
457        public VBA.ExcelVBAModule CodeModule
458        {
459            get
460            {
461                if (_package.Workbook.VbaProject != null)
462                {
463                    return _package.Workbook.VbaProject.Modules[CodeModuleName];
464                }
465                else
466                {
467                    return null;
468                }
469            }
470        }
471        #region WorksheetXml
472    /// <summary>
473    /// The XML document holding the worksheet data.
474        /// All column, row, cell, pagebreak, merged cell and hyperlink-data are loaded into memory and removed from the document when loading the document.       
475    /// </summary>
476    public XmlDocument WorksheetXml
477    {
478      get
479      {
480        return (_worksheetXml);
481      }
482    }
483        internal ExcelVmlDrawingCommentCollection _vmlDrawings = null;
484        /// <summary>
485        /// Vml drawings. underlaying object for comments
486        /// </summary>
487        internal ExcelVmlDrawingCommentCollection VmlDrawingsComments
488        {
489            get
490            {
491                if (_vmlDrawings == null)
492                {
493                    CreateVmlCollection();
494                }
495                return _vmlDrawings;
496            }
497        }
498        internal ExcelCommentCollection _comments = null;
499        /// <summary>
500        /// Collection of comments
501        /// </summary>
502        public ExcelCommentCollection Comments
503        {
504            get
505            {
506                if (_comments == null)
507                {
508                    CreateVmlCollection();
509                    _comments = new ExcelCommentCollection(_package, this, NameSpaceManager);
510                }
511                return _comments;
512            }
513        }
514        private void CreateVmlCollection()
515        {
516            var vmlNode = _worksheetXml.DocumentElement.SelectSingleNode("d:legacyDrawing/@r:id", NameSpaceManager);
517            if (vmlNode == null)
518            {
519                _vmlDrawings = new ExcelVmlDrawingCommentCollection(_package, this, null);
520            }
521            else
522            {
523                if (Part.RelationshipExists(vmlNode.Value))
524                {
525                    var rel = Part.GetRelationship(vmlNode.Value);
526                    var vmlUri = PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
527
528                    _vmlDrawings = new ExcelVmlDrawingCommentCollection(_package, this, vmlUri);
529                    _vmlDrawings.RelId = rel.Id;
530                }
531            }
532        }
533
534        private void CreateXml()
535        {
536            _worksheetXml = new XmlDocument();
537            _worksheetXml.PreserveWhitespace = ExcelPackage.preserveWhitespace;
538            PackagePart packPart = _package.Package.GetPart(WorksheetUri);
539            string xml = "";
540
541            // First Columns, rows, cells, mergecells, hyperlinks and pagebreakes are loaded from a xmlstream to optimize speed...
542            bool doAdjust = _package.DoAdjustDrawings;
543            _package.DoAdjustDrawings = false;
544            Stream stream = packPart.GetStream();
545
546            XmlTextReader xr = new XmlTextReader(stream);
547            xr.ProhibitDtd = true;
548           
549            LoadColumns(xr);    //columnXml
550            long start = stream.Position;
551            LoadCells(xr);
552            long end = stream.Position;
553            LoadMergeCells(xr);
554            LoadHyperLinks(xr);
555            LoadRowPageBreakes(xr);
556            LoadColPageBreakes(xr);
557            //...then the rest of the Xml is extracted and loaded into the WorksheetXml document.
558            stream.Seek(0, SeekOrigin.Begin);
559            Encoding encoding;
560            xml = GetWorkSheetXml(stream, start, end, out encoding);
561
562            //first char is invalid sometimes??
563            if (xml[0] != '<')
564                LoadXmlSafe(_worksheetXml, xml.Substring(1, xml.Length - 1), encoding);
565            else
566                LoadXmlSafe(_worksheetXml, xml, encoding);
567
568            _package.DoAdjustDrawings = doAdjust;
569            ClearNodes();
570        }
571        private void LoadRowPageBreakes(XmlTextReader xr)
572        {
573            if(!ReadUntil(xr, "rowBreaks","colBreaks")) return;
574            while (xr.Read())
575            {
576                if (xr.LocalName == "brk")
577                {
578                    int id;
579                    if (int.TryParse(xr.GetAttribute("id"), out id))
580                    {
581                        Row(id).PageBreak = true;
582                    }
583                }
584                else
585                {
586                    break;
587                }
588            }
589        }
590        private void LoadColPageBreakes(XmlTextReader xr)
591        {
592            if (!ReadUntil(xr, "colBreaks")) return;
593            while (xr.Read())
594            {
595                if (xr.LocalName == "brk")
596                {
597                    int id;
598                    if (int.TryParse(xr.GetAttribute("id"), out id))
599                    {
600                        Column(id).PageBreak = true;
601                    }
602                }
603                else
604                {
605                    break;
606                }
607            }
608        }
609
610        private void ClearNodes()
611        {
612            if (_worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager)!=null)
613            {
614                _worksheetXml.SelectSingleNode("//d:cols", NameSpaceManager).RemoveAll();
615            }
616            if (_worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager) != null)
617            {
618                _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager).RemoveAll();
619            }
620            if (_worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager) != null)
621            {
622                _worksheetXml.SelectSingleNode("//d:hyperlinks", NameSpaceManager).RemoveAll();
623            }
624            if (_worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager) != null)
625            {
626                _worksheetXml.SelectSingleNode("//d:rowBreaks", NameSpaceManager).RemoveAll();
627            }
628            if (_worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager) != null)
629            {
630                _worksheetXml.SelectSingleNode("//d:colBreaks", NameSpaceManager).RemoveAll();
631            }
632        }
633        const int BLOCKSIZE=8192;
634        private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding encoding)
635        {
636            StreamReader sr = new StreamReader(stream);
637            int length = 0;
638            char[] block;
639            int pos;
640            StringBuilder sb = new StringBuilder();
641            Match startmMatch, endMatch;
642            do
643            {
644                int size = stream.Length < BLOCKSIZE ? (int)stream.Length : BLOCKSIZE;
645                block = new char[size];
646                pos = sr.ReadBlock(block, 0, size);                 
647                sb.Append(block,0,pos);
648                length += size;
649            }
650            while (length < start + 20 && length < end);
651            startmMatch = Regex.Match(sb.ToString(), string.Format("(<[^>]*{0}[^>]*>)", "sheetData"));
652            if (!startmMatch.Success) //Not found
653            {
654                encoding = sr.CurrentEncoding;
655                return sb.ToString();
656            }
657            else
658            {
659                string s = sb.ToString();
660                string xml = s.Substring(0, startmMatch.Index);
661                if(startmMatch.Value.EndsWith("/>"))
662                {
663                    xml += s.Substring(startmMatch.Index, s.Length - startmMatch.Index);
664                }
665                else
666                {
667                    if (sr.Peek() != -1)
668                    {
669                        if (end - BLOCKSIZE > 0)
670                        {
671                            long endSeekStart = end - BLOCKSIZE - 4096 < 0 ? 0 : (end - BLOCKSIZE - 4096);
672                            stream.Seek(endSeekStart, SeekOrigin.Begin);
673                            int size = (int)(stream.Length-endSeekStart);
674                            block = new char[size];
675                            sr = new StreamReader(stream);
676                            pos = sr.ReadBlock(block, 0, size);
677                            sb = new StringBuilder();
678                            sb.Append(block, 0, pos);
679                            s = sb.ToString();
680                        }
681                    }
682                    endMatch = Regex.Match(s, string.Format("(</[^>]*{0}[^>]*>)", "sheetData"));
683                    xml += "<sheetData/>" + s.Substring(endMatch.Index + endMatch.Length, s.Length - (endMatch.Index + endMatch.Length));
684                }
685                if (sr.Peek() > -1)
686                {
687                    xml += sr.ReadToEnd();
688                }
689               
690                encoding = sr.CurrentEncoding;
691                return xml;
692            }
693        }
694        private void GetBlockPos(string xml, string tag, ref int start, ref int end)
695        {
696            Match startmMatch, endMatch;
697            startmMatch = Regex.Match(xml.Substring(start), string.Format("(<[^>]*{0}[^>]*>)", tag)); //"<[a-zA-Z:]*" + tag + "[?]*>");
698
699            if (!startmMatch.Success) //Not found
700            {
701                start = -1;
702                end = -1;
703                return;
704            }
705            var startPos=startmMatch.Index+start;
706            if(startmMatch.Value.Substring(startmMatch.Value.Length-2,1)=="/")
707            {
708                end = startPos + startmMatch.Length;
709            }
710            else
711            {
712                endMatch = Regex.Match(xml.Substring(start), string.Format("(</[^>]*{0}[^>]*>)", tag));
713                if (endMatch.Success)
714                {
715                    end = endMatch.Index + endMatch.Length + start;
716                }
717            }
718            start = startPos;
719        }
720        private bool ReadUntil(XmlTextReader xr,params string[] tagName)
721        {
722            if (xr.EOF) return false;
723            while (!Array.Exists(tagName, tag => xr.LocalName.EndsWith(tag)))
724            {
725                xr.Read();
726                if (xr.EOF) return false;
727            }
728            return (xr.LocalName.EndsWith(tagName[0]));
729        }
730        private void LoadColumns (XmlTextReader xr)//(string xml)
731        {
732            var colList = new List<IRangeID>();
733            if (ReadUntil(xr, "cols", "sheetData"))
734            {
735            //if (xml != "")
736            //{
737                //var xr=new XmlTextReader(new StringReader(xml));
738                while(xr.Read())
739                {
740                    if(xr.LocalName!="col") break;
741                    int min = int.Parse(xr.GetAttribute("min"));
742
743                    int style;
744                    if (xr.GetAttribute("style") == null || !int.TryParse(xr.GetAttribute("style"), out style))
745                    {
746                        style = 0;
747                    }
748                    ExcelColumn col = new ExcelColumn(this, min);
749                   
750                    col._columnMax = int.Parse(xr.GetAttribute("max"));
751                    col.StyleID = style;
752                    col.Width = xr.GetAttribute("width") == null ? 0 : double.Parse(xr.GetAttribute("width"), CultureInfo.InvariantCulture);
753                    col.BestFit = xr.GetAttribute("bestFit") != null && xr.GetAttribute("bestFit") == "1" ? true : false;
754                    col.Collapsed = xr.GetAttribute("collapsed") != null && xr.GetAttribute("collapsed") == "1" ? true : false;
755                    col.Phonetic = xr.GetAttribute("phonetic") != null && xr.GetAttribute("phonetic") == "1" ? true : false;
756                    col.OutlineLevel = xr.GetAttribute("outlineLevel") == null ? 0 : int.Parse(xr.GetAttribute("outlineLevel"), CultureInfo.InvariantCulture);
757                    col.Hidden = xr.GetAttribute("hidden") != null && xr.GetAttribute("hidden") == "1" ? true : false;
758                    colList.Add(col);
759                }
760            }
761            _columns = new RangeCollection(colList);
762        }
763        /// <summary>
764        /// Read until the node is found. If not found the xmlreader is reseted.
765        /// </summary>
766        /// <param name="xr">The reader</param>
767        /// <param name="nodeText">Text to search for</param>
768        /// <param name="altNode">Alternative text to search for</param>
769        /// <returns></returns>
770        private static bool ReadXmlReaderUntil(XmlTextReader xr, string nodeText, string altNode)
771        {
772            do
773            {
774                if (xr.LocalName == nodeText || xr.LocalName == altNode) return true;
775            }
776            while(xr.Read());
777            xr.Close();
778            return false;
779        }
780        /// <summary>
781        /// Load Hyperlinks
782        /// </summary>
783        /// <param name="xr">The reader</param>
784        private void LoadHyperLinks(XmlTextReader xr)
785        {
786            if(!ReadUntil(xr, "hyperlinks", "rowBreaks", "colBreaks")) return;
787            while (xr.Read())
788            {
789                if (xr.LocalName == "hyperlink")
790                {
791                    int fromRow, fromCol, toRow, toCol;
792                    ExcelCell.GetRowColFromAddress(xr.GetAttribute("ref"), out fromRow, out fromCol, out toRow, out toCol);
793                    ulong id = ExcelCell.GetCellID(_sheetID, fromRow, fromCol);
794                    ExcelCell cell = _cells[id] as ExcelCell;
795                    if (xr.GetAttribute("id", ExcelPackage.schemaRelationships) != null)
796                    {
797                        cell.HyperLinkRId = xr.GetAttribute("id", ExcelPackage.schemaRelationships);
798                        var uri = Part.GetRelationship(cell.HyperLinkRId).TargetUri;
799                        try
800                        {
801                            if (uri.IsAbsoluteUri)
802                            {
803                                cell.Hyperlink = new ExcelHyperLink(HttpUtility.UrlDecode(uri.OriginalString));
804                            }
805                            else
806                            {
807                                cell.Hyperlink = new ExcelHyperLink(HttpUtility.UrlDecode(uri.OriginalString), UriKind.Relative);
808                            }
809                        }
810                        catch
811                        {
812                            //We should never end up here, but to aviod unhandled exceptions we just set the uri here. JK
813                            cell.Hyperlink = uri;
814                        }
815                        Part.DeleteRelationship(cell.HyperLinkRId); //Delete the relationship, it is recreated when we save the package.
816                    }
817                    else if (xr.GetAttribute("location") != null)
818                    {
819                        ExcelHyperLink hl = new ExcelHyperLink(xr.GetAttribute("location"), xr.GetAttribute("display"));
820                        hl.RowSpann = toRow - fromRow;
821                        hl.ColSpann = toCol - fromCol;
822                        cell.Hyperlink = hl;
823                    }
824                    string tt = xr.GetAttribute("tooltip");
825                    if (!string.IsNullOrEmpty(tt))
826                    {
827                        ((ExcelHyperLink)cell.Hyperlink).ToolTip = tt;
828                    }
829                }
830                else
831                {
832                    break;
833                }
834            }
835        }
836        /// <summary>
837        /// Load cells
838        /// </summary>
839        /// <param name="xr">The reader</param>
840        private void LoadCells(XmlTextReader xr)
841        {
842            var cellList=new List<IRangeID>();
843            var rowList = new List<IRangeID>();
844            var formulaList = new List<IRangeID>();
845            ReadUntil(xr, "sheetData", "mergeCells", "hyperlinks", "rowBreaks", "colBreaks");
846            ExcelCell cell = null;
847            xr.Read();
848           
849            while (!xr.EOF)
850            {
851                while (xr.NodeType == XmlNodeType.EndElement)
852                {
853                    xr.Read();
854                }               
855                if (xr.LocalName == "row")
856                {
857                    int row = Convert.ToInt32(xr.GetAttribute("r"));
858
859                    if (xr.AttributeCount > 2 || (xr.AttributeCount == 2 && xr.GetAttribute("spans") != null))
860                    {
861                        rowList.Add(AddRow(xr, row));
862                    }
863                    xr.Read();
864                }
865                else if (xr.LocalName == "c")
866                {
867                    if (cell != null) cellList.Add(cell);
868                    cell = new ExcelCell(this, xr.GetAttribute("r"));
869                    if (xr.GetAttribute("t") != null) cell.DataType = xr.GetAttribute("t");
870                    cell.StyleID = xr.GetAttribute("s") == null ? 0 : int.Parse(xr.GetAttribute("s"));
871                    xr.Read();
872                }
873                else if (xr.LocalName == "v")
874                {
875                    cell._value = GetValueFromXml(cell, xr);
876                    xr.Read();
877                }
878                else if (xr.LocalName == "f")
879                {
880                    string t = xr.GetAttribute("t");
881                    if (t == null)
882                    {
883                        cell._formula = xr.ReadElementContentAsString();
884                        formulaList.Add(cell);
885                    }
886                    else if (t == "shared")
887                    {
888
889                        string si = xr.GetAttribute("si");
890                        if (si != null)
891                        {
892                            cell._sharedFormulaID = int.Parse(si);
893                            string address = xr.GetAttribute("ref");
894                            string formula = xr.ReadElementContentAsString();
895                            if (formula != "")
896                            {
897                                _sharedFormulas.Add(cell.SharedFormulaID, new Formulas() { Index = cell.SharedFormulaID, Formula = formula, Address = address, StartRow = cell.Row, StartCol = cell.Column });
898                            }
899                        }
900                        else
901                        {
902                            xr.Read();  //Something is wrong in the sheet, read next
903                        }
904                    }
905                    else if (t == "array") //TODO: Array functions are not support yet. Read the formula for the start cell only.
906                    {
907                        string address = xr.GetAttribute("ref");
908                        cell._formula = xr.ReadElementContentAsString();
909                        cell._sharedFormulaID = GetMaxShareFunctionIndex(true); //We use the shared formula id here, just so we can use the same dictionary for both Array and Shared forulas.
910                        _sharedFormulas.Add(cell._sharedFormulaID, new Formulas() { Index = cell._sharedFormulaID, Formula = cell._formula, Address = address, StartRow = cell.Row, StartCol = cell.Column, IsArray = true });
911                    }
912                    else // ??? some other type
913                    {
914                        xr.Read();  //Something is wrong in the sheet, read next
915                    }
916
917                }
918                else if (xr.LocalName == "is")   //Inline string
919                {
920                    xr.Read();
921                    if (xr.LocalName == "t")
922                    {
923                        cell._value = xr.ReadInnerXml();
924                    }
925                    else
926                    {
927                        cell._value = xr.ReadOuterXml();
928                        cell.IsRichText = true;
929                    }
930                }
931                else
932                {
933                    break;
934                }
935            }
936            if (cell != null) cellList.Add(cell);
937
938            _cells = new RangeCollection(cellList);
939            _rows = new RangeCollection(rowList);
940            _formulaCells = new RangeCollection(formulaList);
941        }
942        /// <summary>
943        /// Load merged cells
944        /// </summary>
945        /// <param name="xr"></param>
946        private void LoadMergeCells(XmlTextReader xr)
947        {
948            if(ReadUntil(xr, "mergeCells", "hyperlinks", "rowBreaks", "colBreaks") && !xr.EOF)
949            {
950                while (xr.Read())
951                {
952                    if (xr.LocalName != "mergeCell") break;
953
954                    string address = xr.GetAttribute("ref");
955                    int fromRow, fromCol, toRow, toCol;
956                    ExcelCellBase.GetRowColFromAddress(address, out fromRow, out fromCol, out toRow, out toCol);
957                    for (int row = fromRow; row <= toRow; row++)
958                    {
959                        for (int col = fromCol; col <= toCol; col++)
960                        {
961                            Cell(row, col).Merge = true;
962                        }
963                    }
964
965                    _mergedCells.List.Add(address);
966                }
967            }
968        }
969        /// <summary>
970        /// Update merged cells
971        /// </summary>
972        /// <param name="sw">The writer</param>
973        private void UpdateMergedCells(StreamWriter sw)
974        {
975            sw.Write("<mergeCells>");
976            foreach (string address in _mergedCells)
977            {
978                sw.Write("<mergeCell ref=\"{0}\" />", address);
979            }
980            sw.Write("</mergeCells>");
981        }
982        /// <summary>
983        /// Reads a row from the XML reader
984        /// </summary>
985        /// <param name="xr">The reader</param>
986        /// <param name="row">The row number</param>
987        /// <returns></returns>
988        private ExcelRow AddRow(XmlTextReader xr, int row)
989        {
990            ExcelRow r = new ExcelRow(this, row);
991
992            r.Collapsed = xr.GetAttribute("collapsed") != null && xr.GetAttribute("collapsed")== "1" ? true : false;
993            if (xr.GetAttribute("ht") != null) r.Height = double.Parse(xr.GetAttribute("ht"), CultureInfo.InvariantCulture);
994            r.Hidden = xr.GetAttribute("hidden") != null && xr.GetAttribute("hidden") == "1" ? true : false; ;
995            r.OutlineLevel = xr.GetAttribute("outlineLevel") == null ? 0 : int.Parse(xr.GetAttribute("outlineLevel"), CultureInfo.InvariantCulture); ;
996            r.Phonetic = xr.GetAttribute("ph") != null && xr.GetAttribute("ph") == "1" ? true : false; ;
997            r.StyleID = xr.GetAttribute("s") == null ? 0 : int.Parse(xr.GetAttribute("s"), CultureInfo.InvariantCulture);
998            r.CustomHeight = xr.GetAttribute("customHeight") == null ? false : xr.GetAttribute("customHeight")=="1";
999            return r;
1000        }
1001
1002        private object GetValueFromXml(ExcelCell cell, XmlTextReader xr)
1003        {
1004            object value;
1005            //XmlNode vnode = colNode.SelectSingleNode("d:v", NameSpaceManager);
1006            //if (vnode == null) return null;
1007
1008            if (cell.DataType == "s")
1009            {
1010                int ix = xr.ReadElementContentAsInt();
1011                value = _package.Workbook._sharedStringsList[ix].Text;
1012                cell.IsRichText = _package.Workbook._sharedStringsList[ix].isRichText;
1013            }
1014            else if (cell.DataType == "str")
1015            {
1016                value = xr.ReadElementContentAsString();
1017            }
1018            else if (cell.DataType == "b")
1019            {
1020                value = (xr.ReadElementContentAsString()!="0");
1021            }
1022            else
1023            {
1024                int n = cell.Style.Numberformat.NumFmtID;
1025                string v = xr.ReadElementContentAsString();
1026
1027                if ((n >= 14 && n <= 22) || (n >= 45 && n <= 47))
1028                {
1029                    double res;
1030                    if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out res))
1031                    {
1032                        value = DateTime.FromOADate(res);
1033                    }
1034                    else
1035                    {
1036                        value = "";
1037                    }
1038                }
1039                else
1040                {
1041                    double d;
1042                    if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
1043                    {
1044                        value = d;
1045                    }
1046                    else
1047                    {
1048                        value = double.NaN;
1049                    }
1050                }
1051            }
1052            return value;
1053        }
1054        //private string GetSharedString(int stringID)
1055        //{
1056        //    string retValue = null;
1057        //    XmlNodeList stringNodes = xlPackage.Workbook.SharedStringsXml.SelectNodes(string.Format("//d:si", stringID), NameSpaceManager);
1058        //    XmlNode stringNode = stringNodes[stringID];
1059        //    if (stringNode != null)
1060        //        retValue = stringNode.InnerText;
1061        //    return (retValue);
1062        //}
1063        #endregion
1064    #region HeaderFooter
1065    /// <summary>
1066    /// A reference to the header and footer class which allows you to
1067    /// set the header and footer for all odd, even and first pages of the worksheet
1068        /// </summary>
1069        /// <remarks>
1070        /// To format the text you can use the following format
1071        /// <list type="table">
1072        /// <listheader><term>Prefix</term><description>Description</description></listheader>
1073        /// <item><term>&amp;U</term><description>Underlined</description></item>
1074        /// <item><term>&amp;E</term><description>Double Underline</description></item>
1075        /// <item><term>&amp;K:xxxxxx</term><description>Color. ex &amp;K:FF0000 for red</description></item>
1076        /// <item><term>&amp;"Font,Regular Bold Italic"</term><description>Changes the font. Regular or Bold or Italic or Bold Italic can be used. ex &amp;"Arial,Bold Italic"</description></item>
1077        /// <item><term>&amp;nn</term><description>Change font size. nn is an integer. ex &amp;24</description></item>
1078        /// <item><term>&amp;G</term><description>Placeholder for images. Images can not be added by the library, but its possible to use in a template.</description></item>
1079        /// </list>
1080        /// </remarks>
1081        public ExcelHeaderFooter HeaderFooter
1082    {
1083      get
1084      {
1085        if (_headerFooter == null)
1086        {
1087                    XmlNode headerFooterNode = TopNode.SelectSingleNode("d:headerFooter", NameSpaceManager);
1088                    if (headerFooterNode == null)
1089                        headerFooterNode= CreateNode("d:headerFooter");
1090                    _headerFooter = new ExcelHeaderFooter(NameSpaceManager, headerFooterNode, this);
1091        }               
1092        return (_headerFooter);
1093      }
1094    }
1095    #endregion
1096
1097        #region "PrinterSettings"
1098        /// <summary>
1099        /// Printer settings
1100        /// </summary>
1101        public ExcelPrinterSettings PrinterSettings
1102        {
1103            get
1104            {
1105                var ps = new ExcelPrinterSettings(NameSpaceManager, TopNode, this);
1106                ps.SchemaNodeOrder = SchemaNodeOrder;
1107                return ps;
1108            }
1109        }
1110        #endregion
1111
1112    #endregion // END Worksheet Public Properties
1113
1114    #region Worksheet Public Methods
1115       
1116        /// <summary>
1117    /// Provides access to an individual cell within the worksheet.
1118    /// </summary>
1119    /// <param name="row">The row number in the worksheet</param>
1120    /// <param name="col">The column number in the worksheet</param>
1121    /// <returns></returns>   
1122        internal ExcelCell Cell(int row, int col)
1123        {
1124            ulong cellID=ExcelCell.GetCellID(SheetID, row, col);
1125            if (!_cells.ContainsKey(cellID))
1126            {
1127                _cells.Add(new ExcelCell(this, row, col));
1128            }
1129            return _cells[cellID] as ExcelCell;
1130        }
1131        /// <summary>
1132        /// Provides access to a range of cells
1133        /// </summary> 
1134        public ExcelRange Cells
1135        {
1136            get
1137            {
1138                return new ExcelRange(this, 1, 1, ExcelPackage.MaxRows, ExcelPackage.MaxColumns);
1139            }
1140        }
1141        /// <summary>
1142        /// Provides access to the selected range of cells
1143        /// </summary> 
1144        public ExcelRange SelectedRange
1145        {
1146            get
1147            {
1148                return new ExcelRange(this, View.SelectedRange);
1149            }
1150        }
1151        MergeCellsCollection<string> _mergedCells = new MergeCellsCollection<string>();
1152        /// <summary>
1153        /// Addresses to merged ranges
1154        /// </summary>
1155        public MergeCellsCollection<string> MergedCells
1156        {
1157            get
1158            {
1159                return _mergedCells;
1160            }
1161        }
1162        /// <summary>
1163    /// Provides access to an individual row within the worksheet so you can set its properties.
1164    /// </summary>
1165    /// <param name="row">The row number in the worksheet</param>
1166    /// <returns></returns>
1167    public ExcelRow Row(int row)
1168    {
1169            ExcelRow r;
1170            ulong id = ExcelRow.GetRowID(_sheetID, row);
1171            if (_rows.ContainsKey(id))
1172            {
1173                r = _rows[id] as ExcelRow;
1174            }
1175            else
1176            {
1177                r = new ExcelRow(this, row);
1178                _rows.Add(r);
1179            }
1180            return r;
1181    }
1182    /// <summary>
1183    /// Provides access to an individual column within the worksheet so you can set its properties.
1184    /// </summary>
1185    /// <param name="col">The column number in the worksheet</param>
1186    /// <returns></returns>
1187    public ExcelColumn Column(int col)
1188    {
1189            ExcelColumn column;
1190            ulong id=ExcelColumn.GetColumnID(_sheetID, col);
1191            if (_columns.ContainsKey(id))
1192            {
1193                column = _columns[id] as ExcelColumn;
1194                if (column.ColumnMin != column.ColumnMax)
1195                {
1196                    int maxCol = column.ColumnMax;
1197                    column.ColumnMax=col;
1198                    ExcelColumn copy = CopyColumn(column, col + 1, maxCol);
1199                }
1200            }
1201            else
1202            {
1203                foreach (ExcelColumn checkColumn in _columns)
1204                {
1205                    if (col > checkColumn.ColumnMin && col <= checkColumn.ColumnMax)
1206                    {
1207                        int maxCol = checkColumn.ColumnMax;
1208                        checkColumn.ColumnMax = col - 1;
1209                        if (maxCol > col)
1210                        {
1211                            ExcelColumn newC = CopyColumn(checkColumn, col + 1, maxCol);
1212                        }
1213                        return CopyColumn(checkColumn, col,col);                       
1214                    }
1215                }
1216                column = new ExcelColumn(this, col);
1217                _columns.Add(column);
1218             }
1219            return column;
1220    }
1221        /// <summary>
1222        /// Returns the name of the worksheet
1223        /// </summary>
1224        /// <returns>The name of the worksheet</returns>
1225        public override string ToString()
1226        {
1227            return Name;
1228        }
1229        internal ExcelColumn CopyColumn(ExcelColumn c, int col, int maxCol)
1230        {
1231            ExcelColumn newC = new ExcelColumn(this, col);
1232            newC.ColumnMax = maxCol;
1233            if (c.StyleName != "")
1234                newC.StyleName = c.StyleName;
1235            else
1236                newC.StyleID = c.StyleID;
1237
1238            newC._width = c._width;
1239            newC._hidden = c.Hidden;
1240            newC.OutlineLevel = c.OutlineLevel;
1241            newC.Phonetic = c.Phonetic;
1242            newC.BestFit = c.BestFit;
1243            _columns.Add(newC);
1244            return newC;
1245       }
1246        /// <summary>
1247        /// Selects a range in the worksheet. The active cell is the topmost cell.
1248        /// Make the current worksheet active.
1249        /// </summary>
1250        /// <param name="Address">An address range</param>
1251        public void Select(string Address)
1252        {
1253            Select(Address, true);
1254        }
1255        /// <summary>
1256        /// Selects a range in the worksheet. The actice cell is the topmost cell.
1257        /// </summary>
1258        /// <param name="Address">A range of cells</param>
1259        /// <param name="SelectSheet">Make the sheet active</param>
1260        public void Select(string Address, bool SelectSheet)
1261        {
1262            int fromCol, fromRow, toCol, toRow;
1263            //Get rows and columns and validate as well
1264            ExcelCell.GetRowColFromAddress(Address, out fromRow, out fromCol, out toRow, out toCol);
1265
1266            if (SelectSheet)
1267            {
1268                View.TabSelected = true;
1269            }
1270            View.SelectedRange = Address;
1271            View.ActiveCell = ExcelCell.GetAddress(fromRow, fromCol);           
1272        }
1273        /// <summary>
1274        /// Selects a range in the worksheet. The active cell is the topmost cell of the first address.
1275        /// Make the current worksheet active.
1276        /// </summary>
1277        /// <param name="Address">An address range</param>
1278        public void Select(ExcelAddress Address)
1279        {
1280            Select(Address, true);
1281        }
1282        /// <summary>
1283        /// Selects a range in the worksheet. The active cell is the topmost cell of the first address.
1284        /// </summary>
1285        /// <param name="Address">A range of cells</param>
1286        /// <param name="SelectSheet">Make the sheet active</param>
1287        public void Select(ExcelAddress Address, bool SelectSheet)
1288        {
1289
1290            if (SelectSheet)
1291            {
1292                View.TabSelected = true;
1293            }
1294            string selAddress = ExcelCellBase.GetAddress(Address.Start.Row, Address.Start.Column) + ":" + ExcelCellBase.GetAddress(Address.End.Row, Address.End.Column);
1295            if (Address.Addresses != null)
1296            {
1297                foreach (var a in Address.Addresses)
1298                {
1299                    selAddress += " " + ExcelCellBase.GetAddress(a.Start.Row, a.Start.Column) + ":" + ExcelCellBase.GetAddress(a.End.Row, a.End.Column);
1300                }
1301            }
1302            View.SelectedRange = selAddress;
1303            View.ActiveCell = ExcelCell.GetAddress(Address.Start.Row, Address.Start.Column);
1304        }
1305
1306    #region InsertRow
1307        /// <summary>
1308        /// Inserts a new row into the spreadsheet.  Existing rows below the position are
1309        /// shifted down.  All formula are updated to take account of the new row.
1310        /// </summary>
1311        /// <param name="rowFrom">The position of the new row</param>
1312        /// <param name="rows">Number of rows to insert</param>
1313        public void InsertRow(int rowFrom, int rows)
1314        {
1315            InsertRow(rowFrom, rows, 0);
1316        }
1317        /// <summary>
1318    /// Inserts a new row into the spreadsheet.  Existing rows below the position are
1319    /// shifted down.  All formula are updated to take account of the new row.
1320    /// </summary>
1321        /// <param name="rowFrom">The position of the new row</param>
1322        /// <param name="rows">Number of rows to insert.</param>
1323        /// <param name="copyStylesFromRow">Copy Styles from this row. Applied to all inserted rows</param>
1324    public void InsertRow(int rowFrom, int rows, int copyStylesFromRow)
1325    {
1326            //Insert the new row into the collection
1327            ulong copyRowID=ExcelRow.GetRowID(SheetID, copyStylesFromRow);
1328            List<ExcelCell> copyStylesCells=new List<ExcelCell>();
1329            if (copyStylesFromRow > 0)
1330            {
1331                int startIndex = _cells.IndexOf(copyRowID);
1332                startIndex = ~startIndex;
1333                while(startIndex < _cells.Count && (_cells[startIndex] as ExcelCell).Row==copyStylesFromRow)
1334                {
1335                    copyStylesCells.Add(_cells[startIndex++] as ExcelCell);
1336                }
1337            }
1338            ulong rowID=ExcelRow.GetRowID(SheetID, rowFrom);
1339
1340            _cells.InsertRows(rowID, rows);
1341            _rows.InsertRows(rowID, rows);
1342            _formulaCells.InsertRowsUpdateIndex(rowID, rows);
1343            if (_comments != null) _comments._comments.InsertRowsUpdateIndex(rowID, rows);
1344            if (_vmlDrawings != null) _vmlDrawings._drawings.InsertRowsUpdateIndex(rowID, rows);
1345
1346            foreach (ExcelCell cell in _formulaCells)
1347            {
1348                if (cell.SharedFormulaID < 0)
1349                {
1350                    cell.Formula = ExcelCell.UpdateFormulaReferences(cell.Formula, rows, 0, rowFrom, 0);
1351                }
1352                else
1353                {
1354                    throw new Exception("Shared formula error");
1355                }
1356            }
1357
1358            FixSharedFormulasRows(rowFrom, rows);
1359           
1360            FixMergedCells(rowFrom, rows,false);
1361
1362            //Copy the styles
1363            foreach (ExcelCell cell in copyStylesCells)
1364            {
1365                Cells[rowFrom, cell.Column, rowFrom + rows - 1, cell.Column].StyleID = cell.StyleID;
1366            }
1367        }
1368        /// <summary>
1369        /// Adds a value to the row of merged cells to fix for inserts or deletes
1370        /// </summary>
1371        /// <param name="position"></param>
1372        /// <param name="rows"></param>
1373        /// <param name="delete"></param>
1374        private void FixMergedCells(int position, int rows, bool delete)
1375        {
1376            List<int> removeIndex = new List<int>();
1377            for (int i = 0; i < _mergedCells.Count; i++)
1378            {
1379                ExcelAddressBase addr = new ExcelAddressBase(_mergedCells[i]), newAddr ;
1380                if (delete)
1381                {
1382                    newAddr=addr.DeleteRow(position, rows);
1383                    if (newAddr == null)
1384                    {
1385                        removeIndex.Add(i);
1386                        continue;
1387                    }
1388                }
1389                else
1390                {
1391                    newAddr = addr.AddRow(position, rows);
1392                }
1393               
1394                //The address has changed.
1395                if (newAddr._address != addr._address)
1396                {
1397                    //Set merged prop for cells
1398                    for (int row = newAddr._fromRow; row <= newAddr._toRow; row++)
1399                    {
1400                        for (int col = newAddr._fromCol; col <= newAddr._toCol; col++)
1401                        {
1402                            Cell(row, col).Merge = true;
1403                        }
1404                    }
1405                }
1406
1407                _mergedCells.List[i] = newAddr._address;
1408            }
1409            for (int i = removeIndex.Count - 1; i >= 0; i--)
1410            {
1411                _mergedCells.List.RemoveAt(removeIndex[i]);
1412            }
1413        }
1414        private void FixSharedFormulasRows(int position, int rows)
1415        {
1416            List<Formulas> added = new List<Formulas>();
1417            List<Formulas> deleted = new List<Formulas>();
1418
1419            foreach (int id in _sharedFormulas.Keys)
1420            {
1421                var f = _sharedFormulas[id];
1422                int fromCol, fromRow, toCol, toRow;
1423
1424                ExcelCell.GetRowColFromAddress(f.Address, out fromRow, out fromCol, out toRow, out toCol);
1425                if (position >= fromRow && position+(Math.Abs(rows)) <= toRow) //Insert/delete is whithin the share formula address
1426                {
1427                    if (rows > 0) //Insert
1428                    {
1429                        f.Address = ExcelCell.GetAddress(fromRow, fromCol) + ":" + ExcelCell.GetAddress(position - 1, toCol);
1430                        if (toRow != fromRow)
1431                        {
1432                            Formulas newF = new Formulas();
1433                            newF.StartCol = f.StartCol;
1434                            newF.StartRow = position + rows;
1435                            newF.Address = ExcelCell.GetAddress(position + rows, fromCol) + ":" + ExcelCell.GetAddress(toRow + rows, toCol);
1436                            newF.Formula = ExcelCell.TranslateFromR1C1(ExcelCell.TranslateToR1C1(f.Formula, f.StartRow, f.StartCol), position, f.StartCol);
1437                            added.Add(newF);
1438                        }
1439                    }
1440                    else
1441                    {
1442                        if (fromRow - rows < toRow)
1443                        {
1444                            f.Address = ExcelCell.GetAddress(fromRow, fromCol, toRow+rows, toCol);
1445                        }
1446                        else
1447                        {
1448                            f.Address = ExcelCell.GetAddress(fromRow, fromCol) + ":" + ExcelCell.GetAddress(toRow + rows, toCol);
1449                        }
1450                    }
1451                }
1452                else if (position <= toRow)
1453                {
1454                    if (rows > 0) //Insert before shift down
1455                    {
1456                        f.StartRow += rows;
1457                        //f.Formula = ExcelCell.UpdateFormulaReferences(f.Formula, rows, 0, position, 0); //Recalc the cells positions
1458                        f.Address = ExcelCell.GetAddress(fromRow + rows, fromCol) + ":" + ExcelCell.GetAddress(toRow + rows, toCol);
1459                    }
1460                    else
1461                    {
1462                        //Cells[f.Address].SetSharedFormulaID(int.MinValue);
1463                        if (position <= fromRow && position + Math.Abs(rows) > toRow)  //Delete the formula
1464                        {
1465                            deleted.Add(f);
1466                        }
1467                        else
1468                        {
1469                            toRow = toRow + rows < position - 1 ? position - 1 : toRow + rows;
1470                            if (position <= fromRow)
1471                            {
1472                                fromRow = fromRow + rows < position ? position : fromRow + rows;
1473                            }
1474
1475                            f.Address = ExcelCell.GetAddress(fromRow, fromCol, toRow, toCol);
1476                            Cells[f.Address].SetSharedFormulaID(f.Index);
1477                            //f.StartRow = fromRow;
1478
1479                            //f.Formula = ExcelCell.UpdateFormulaReferences(f.Formula, rows, 0, position, 0);
1480                       
1481                        }
1482                    }
1483                }
1484            }
1485
1486            AddFormulas(added, position, rows);
1487
1488            //Remove formulas
1489            foreach (Formulas f in deleted)
1490            {
1491                _sharedFormulas.Remove(f.Index);
1492            }
1493
1494            //Fix Formulas
1495            added = new List<Formulas>();
1496            foreach (int id in _sharedFormulas.Keys)
1497            {
1498                var f = _sharedFormulas[id];
1499                UpdateSharedFormulaRow(ref f, position, rows, ref added);
1500            }
1501            AddFormulas(added, position, rows);
1502        }
1503
1504        private void AddFormulas(List<Formulas> added, int position, int rows)
1505        {
1506            //Add new formulas
1507            foreach (Formulas f in added)
1508            {
1509                f.Index = GetMaxShareFunctionIndex(false);
1510                _sharedFormulas.Add(f.Index, f);
1511                Cells[f.Address].SetSharedFormulaID(f.Index);
1512            }
1513        }
1514
1515        private void UpdateSharedFormulaRow(ref Formulas formula, int startRow, int rows, ref List<Formulas> newFormulas)
1516        {
1517            int fromRow,fromCol, toRow, toCol;
1518            int newFormulasCount = newFormulas.Count;
1519            ExcelCellBase.GetRowColFromAddress(formula.Address, out fromRow, out fromCol, out toRow, out toCol);
1520            //int refSplits = Regex.Split(formula.Formula, "#REF!").GetUpperBound(0);
1521            string formualR1C1;
1522            if (rows > 0 || fromRow <= startRow)
1523            {
1524                formualR1C1 = ExcelRangeBase.TranslateToR1C1(formula.Formula, formula.StartRow, formula.StartCol);
1525                formula.Formula = ExcelRangeBase.TranslateFromR1C1(formualR1C1, fromRow, formula.StartCol);
1526            }
1527            else
1528            {
1529                formualR1C1 = ExcelRangeBase.TranslateToR1C1(formula.Formula, formula.StartRow-rows, formula.StartCol);
1530                formula.Formula = ExcelRangeBase.TranslateFromR1C1(formualR1C1, formula.StartRow, formula.StartCol);
1531            }
1532            //bool isRef = false;
1533            //Formulas restFormula=formula;
1534            string prevFormualR1C1 = formualR1C1;
1535            for (int row = fromRow; row <= toRow; row++)
1536            {
1537                for (int col = fromCol; col <= toCol; col++)
1538                {
1539                    string newFormula;
1540                    string currentFormulaR1C1;
1541                    if (rows > 0 || row < startRow)
1542                    {
1543                        newFormula = ExcelCellBase.UpdateFormulaReferences(ExcelCell.TranslateFromR1C1(formualR1C1, row, col), rows, 0, startRow, 0);
1544                        currentFormulaR1C1 = ExcelRangeBase.TranslateToR1C1(newFormula, row, col);
1545                    }
1546                    else
1547                    {
1548                        newFormula = ExcelCellBase.UpdateFormulaReferences(ExcelCell.TranslateFromR1C1(formualR1C1, row-rows, col), rows, 0, startRow, 0);
1549                        currentFormulaR1C1 = ExcelRangeBase.TranslateToR1C1(newFormula, row, col);
1550                    }
1551                    if (currentFormulaR1C1 != prevFormualR1C1) //newFormula.Contains("#REF!"))
1552                    {
1553                        //if (refSplits == 0 || Regex.Split(newFormula, "#REF!").GetUpperBound(0) != refSplits)
1554                        //{
1555                        //isRef = true;
1556                        if (row == fromRow && col == fromCol)
1557                        {
1558                            formula.Formula = newFormula;
1559                        }
1560                        else
1561                        {
1562                            if (newFormulas.Count == newFormulasCount)
1563                            {
1564                                formula.Address = ExcelCellBase.GetAddress(formula.StartRow, formula.StartCol, row - 1, col);
1565                            }
1566                            else
1567                            {
1568                                newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[newFormulas.Count - 1].StartCol, row - 1, col);
1569                            }
1570                            var refFormula = new Formulas();
1571                            refFormula.Formula = newFormula;
1572                            refFormula.StartRow = row;
1573                            refFormula.StartCol = col;
1574                            newFormulas.Add(refFormula);
1575
1576                            //restFormula = null;
1577                            prevFormualR1C1 = currentFormulaR1C1;
1578                        }
1579                    }
1580                    //    }
1581                    //    else
1582                    //    {
1583                    //        isRef = false;
1584                    //    }
1585                    //}
1586                    //else
1587                    //{
1588                    //    isRef = false;
1589                    //}
1590                    //if (restFormula==null)
1591                    //{
1592                        //if (newFormulas.Count == newFormulasCount)
1593                        //{
1594                        //    formula.Address = ExcelCellBase.GetAddress(formula.StartRow, formula.StartCol, row - 1, col);
1595                        //}
1596                        //else
1597                        //{
1598//                            newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[0].StartCol, row - 1, col);
1599                        //}
1600
1601                        //restFormula = new Formulas();
1602                        //restFormula.Formula = newFormula;
1603                        //restFormula.StartRow = row;
1604                        //restFormula.StartCol = col;
1605                        //newFormulas.Add(restFormula);
1606                    //}
1607                }
1608            }
1609            if (rows < 0 && formula.StartRow > startRow)
1610            {
1611                if (formula.StartRow + rows < startRow)
1612                {
1613                    formula.StartRow = startRow;
1614                }
1615                else
1616                {
1617                    formula.StartRow += rows;
1618                }
1619            }
1620            if (newFormulas.Count > newFormulasCount)
1621            {
1622                newFormulas[newFormulas.Count - 1].Address = ExcelCellBase.GetAddress(newFormulas[newFormulas.Count - 1].StartRow, newFormulas[newFormulas.Count - 1].StartCol, toRow, toCol);
1623            }
1624        }
1625        #endregion
1626
1627        #region DeleteRow
1628        /// <summary>
1629        /// Deletes the specified row from the worksheet.
1630        /// </summary>
1631        /// <param name="rowFrom">The number of the start row to be deleted</param>
1632        /// <param name="rows">Number of rows to delete</param>
1633        public void DeleteRow(int rowFrom, int rows)
1634        {
1635            ulong rowID = ExcelRow.GetRowID(SheetID, rowFrom);
1636
1637            _cells.DeleteRows(rowID, rows, true);
1638            _rows.DeleteRows(rowID, rows, true);
1639            _formulaCells.DeleteRows(rowID, rows, false);
1640            if (_comments != null) _comments._comments.DeleteRows(rowID, rows, false);
1641            if (_vmlDrawings != null) _vmlDrawings._drawings.DeleteRows(rowID, rows, false);
1642
1643            foreach (ExcelCell cell in _formulaCells)
1644            {
1645                cell._formula = ExcelCell.UpdateFormulaReferences(cell.Formula, -rows, 0, rowFrom, 0);
1646                cell._formulaR1C1 = "";
1647            }
1648            FixSharedFormulasRows(rowFrom, -rows);
1649            FixMergedCells(rowFrom, rows,true);
1650        }
1651        /// <summary>
1652        /// Deletes the specified row from the worksheet.
1653        /// </summary>
1654        /// <param name="rowFrom">The number of the start row to be deleted</param>
1655        /// <param name="rows">Number of rows to delete</param>
1656        /// <param name="shiftOtherRowsUp">Not used. Rows are always shifted</param>
1657        public void DeleteRow(int rowFrom, int rows, bool shiftOtherRowsUp)
1658    {
1659            if (shiftOtherRowsUp)
1660            {
1661                DeleteRow(rowFrom, rows);
1662            }
1663            else
1664            {
1665                ulong rowID = ExcelRow.GetRowID(SheetID, rowFrom);
1666                _cells.DeleteRows(rowID, rows, true);
1667                _rows.DeleteRows(rowID, rows, true);
1668                _formulaCells.DeleteRows(rowID, rows, false);
1669                if (_comments != null) _comments._comments.DeleteRows(rowID, rows, false);
1670                if (_vmlDrawings != null) _vmlDrawings._drawings.DeleteRows(rowID, rows, false);
1671            }
1672        }
1673    #endregion
1674
1675        /// <summary>
1676        /// Get the cell value from thw worksheet
1677        /// </summary>
1678        /// <param name="Row">The row number</param>
1679        /// <param name="Column">The row number</param>
1680        /// <returns>The value</returns>
1681        public object GetValue(int Row, int Column)
1682        {
1683            ulong cellID = ExcelCell.GetCellID(SheetID, Row, Column);
1684
1685            if (_cells.ContainsKey(cellID))
1686            {
1687                var cell = ((ExcelCell)_cells[cellID]);
1688                if (cell.IsRichText)
1689                {
1690                    return (object)Cells[Row, Column].RichText.Text;
1691                }
1692                else
1693                {
1694                    return cell.Value;
1695                }
1696            }
1697            else
1698            {
1699                return null;
1700            }
1701        }
1702
1703        /// <summary>
1704        /// Get a strongly typed cell value from the worksheet
1705        /// </summary>
1706        /// <typeparam name="T">The type</typeparam>
1707        /// <param name="Row">The row number</param>
1708        /// <param name="Column">The row number</param>
1709        /// <returns>The value. If the value can't be converted to the specified type, the default value will be returned</returns>
1710        public T GetValue<T>(int Row, int Column)
1711        {
1712             ulong cellID=ExcelCell.GetCellID(SheetID, Row, Column);
1713                       
1714            if (!_cells.ContainsKey(cellID))
1715            {
1716                return default(T);
1717            }
1718
1719            var cell=((ExcelCell)_cells[cellID]);
1720            if (cell.IsRichText)
1721            {
1722                return (T)(object)Cells[Row, Column].RichText.Text;
1723            }
1724            else
1725            {
1726                return GetTypedValue<T>(cell.Value);
1727            }
1728        }
1729        //Thanks to Michael Tran for parts of this method
1730        internal T GetTypedValue<T>(object v)
1731        {
1732            if (v == null)
1733            {
1734                return default(T);
1735            }
1736            Type fromType = v.GetType();
1737            Type toType = typeof(T);
1738            if (fromType == toType)
1739            {
1740                return (T)v;
1741            }
1742            var cnv = TypeDescriptor.GetConverter(fromType);
1743            if (toType == typeof(DateTime))    //Handle dates
1744            {
1745                if (fromType == typeof(TimeSpan))
1746                {
1747                    return ((T)(object)(new DateTime(((TimeSpan)v).Ticks)));
1748                }
1749                else if (fromType == typeof(string))
1750                {
1751                    DateTime dt;
1752                    if (DateTime.TryParse(v.ToString(), out dt))
1753                    {
1754                        return (T)(object)(dt);
1755                    }
1756                    else
1757                    {
1758                        return default(T);
1759                    }
1760
1761                }
1762                else
1763                {
1764                    if (cnv.CanConvertTo(typeof(double)))
1765                    {
1766                        return (T)(object)(DateTime.FromOADate((double)cnv.ConvertTo(v, typeof(double))));
1767                    }
1768                    else
1769                    {
1770                        return default(T);
1771                    }
1772                }
1773            }
1774            else if (toType == typeof(TimeSpan))    //Handle timespan
1775            {
1776                if (fromType == typeof(DateTime))
1777                {
1778                    return ((T)(object)(new TimeSpan(((DateTime)v).Ticks)));
1779                }
1780                else if (fromType == typeof(string))
1781                {
1782                    TimeSpan ts;
1783                    if (TimeSpan.TryParse(v.ToString(), out ts))
1784                    {
1785                        return (T)(object)(ts);
1786                    }
1787                    else
1788                    {
1789                        return default(T);
1790                    }
1791                }
1792                else
1793                {
1794                    if (cnv.CanConvertTo(typeof(double)))
1795                    {
1796
1797                        return (T)(object)(new TimeSpan(DateTime.FromOADate((double)cnv.ConvertTo(v, typeof(double))).Ticks));
1798                    }
1799                    else
1800                    {
1801                        try
1802                        {
1803                            // Issue 14682 -- "GetValue<decimal>() won't convert strings"
1804                            // As suggested, after all special cases, all .NET to do it's
1805                            // preferred conversion rather than simply returning the default
1806                            return (T)Convert.ChangeType(v, typeof(T));
1807                        }
1808                        catch (Exception)
1809                        {
1810                            // This was the previous behaviour -- no conversion is available.
1811                            return default(T);
1812                        }
1813                    }
1814                }
1815            }
1816            else
1817            {
1818                if (cnv.CanConvertTo(toType))
1819                {
1820                    return (T)cnv.ConvertTo(v, typeof(T));
1821                }
1822                else
1823                {
1824                    if (toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
1825                    {
1826                        toType = Nullable.GetUnderlyingType(toType);
1827                        if (cnv.CanConvertTo(toType))
1828                        {
1829                            return (T)cnv.ConvertTo(v, typeof(T));
1830                        }
1831                    }
1832
1833                    if(fromType==typeof(double) && toType==typeof(decimal))
1834                    {
1835                        return (T)(object)Convert.ToDecimal(v);
1836                    }
1837                    else if (fromType == typeof(decimal) && toType == typeof(double))
1838                    {
1839                        return (T)(object)Convert.ToDouble(v);
1840                    }
1841                    else
1842                    {
1843                        return default(T);
1844                    }
1845                }
1846            }
1847        }
1848        /// <summary>
1849        /// Set the value of a cell
1850        /// </summary>
1851        /// <param name="Row">The row number</param>
1852        /// <param name="Column">The column number</param>
1853        /// <param name="Value">The value</param>
1854        public void SetValue(int Row, int Column, object Value)
1855        {
1856            Cell(Row, Column).Value = Value;
1857        }
1858        /// <summary>
1859        /// Set the value of a cell
1860        /// </summary>
1861        /// <param name="Address">The Excel address</param>
1862        /// <param name="Value">The value</param>
1863        public void SetValue(string Address, object Value)
1864        {
1865            int row, col;
1866            ExcelAddressBase.GetRowCol(Address, out row, out col, true);
1867            if (row < 1 || col < 1 || row > ExcelPackage.MaxRows && col > ExcelPackage.MaxColumns)
1868            {
1869                throw new ArgumentOutOfRangeException("Address is invalid or out of range");
1870            }
1871            Cell(row, col).Value = Value;
1872        }
1873
1874        #region MergeCellId
1875
1876        /// <summary>
1877        /// Get MergeCell Index No
1878        /// </summary>
1879        /// <param name="row"></param>
1880        /// <param name="column"></param>
1881        /// <returns></returns>
1882        public int GetMergeCellId(int row, int column)
1883        {
1884            for (int i = 0; i < _mergedCells.Count; i++)
1885            {
1886                ExcelRange range = Cells[_mergedCells[i]];
1887
1888                if (range.Start.Row <= row && row <= range.End.Row)
1889                {
1890                    if (range.Start.Column <= column && column <= range.End.Column)
1891                    {
1892                        return i + 1;
1893                    }
1894                }
1895            }
1896            return 0;
1897        }
1898
1899        #endregion
1900    #endregion // END Worksheet Public Methods
1901
1902    #region Worksheet Private Methods
1903
1904    #region Worksheet Save
1905    /// <summary>
1906    /// Saves the worksheet to the package.
1907    /// </summary>
1908    internal void Save()  // Worksheet Save
1909    {
1910            DeletePrinterSettings();
1911
1912      if (_worksheetXml != null)
1913      {
1914                if (TopNode.Name == "chartsheet")
1915                {
1916                    return;
1917                }
1918        // save the header & footer (if defined)
1919        if (_headerFooter != null)
1920          HeaderFooter.Save();
1921
1922                if (_cells.Count > 0)
1923                {
1924                    this.SetXmlNodeString("d:dimension/@ref", Dimension.Address);
1925                }
1926
1927                if (_drawings != null && _drawings.Count == 0)
1928                {
1929                    //Remove node if no drawings exists.
1930                    DeleteNode("d:drawing");
1931                }
1932
1933                SaveComments();
1934                HeaderFooter.SaveHeaderFooterImages();
1935                SaveTables();
1936                SavePivotTables();
1937                SaveXml();
1938      }
1939           
1940            if (Drawings.UriDrawing!=null)
1941            {
1942                if (Drawings.Count == 0)
1943                {                   
1944                    Part.DeleteRelationship(Drawings._drawingRelation.Id);
1945                    _package.Package.DeletePart(Drawings.UriDrawing);                   
1946                }
1947                else
1948                {
1949                    PackagePart partPack = Drawings.Part;
1950                    Drawings.DrawingXml.Save(partPack.GetStream(FileMode.Create, FileAccess.Write));
1951                    foreach (ExcelDrawing d in Drawings)
1952                    {
1953                        if (d is ExcelChart)
1954                        {
1955                            ExcelChart c = (ExcelChart)d;
1956                            c.ChartXml.Save(c.Part.GetStream(FileMode.Create, FileAccess.Write));
1957                        }
1958                    }
1959                }
1960            }
1961    }
1962
1963        /// <summary>
1964        /// Delete the printersettings relationship and part.
1965        /// </summary>
1966        private void DeletePrinterSettings()
1967        {
1968            //Delete the relationship from the pageSetup tag
1969            XmlAttribute attr = (XmlAttribute)WorksheetXml.SelectSingleNode("//d:pageSetup/@r:id", NameSpaceManager);
1970            if (attr != null)
1971            {
1972                string relID = attr.Value;
1973                //First delete the attribute from the XML
1974                attr.OwnerElement.Attributes.Remove(attr);
1975                if(Part.RelationshipExists(relID))
1976                {
1977                    PackageRelationship rel = Part.GetRelationship(relID);
1978                    Uri printerSettingsUri = PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
1979                    Part.DeleteRelationship(rel.Id);
1980
1981                    //Delete the part from the package
1982                    if(_package.Package.PartExists(printerSettingsUri))
1983                    {
1984                        _package.Package.DeletePart(printerSettingsUri);
1985                    }
1986                }
1987            }
1988        }
1989        private void SaveComments()
1990        {
1991            if (_comments != null)
1992            {
1993                if (_comments.Count == 0)
1994                {
1995                    if (_comments.Uri != null)
1996                    {
1997                        Part.DeleteRelationship(_comments.RelId);
1998                        _package.Package.DeletePart(_comments.Uri);                       
1999                    }
2000                    RemoveLegacyDrawingRel(VmlDrawingsComments.RelId);
2001                }
2002                else
2003                {
2004                    if (_comments.Uri == null)
2005                    {
2006                        _comments.Uri=new Uri(string.Format(@"/xl/comments{0}.xml", SheetID), UriKind.Relative);                       
2007                    }
2008                    if(_comments.Part==null)
2009                    {
2010                        _comments.Part = _package.Package.CreatePart(_comments.Uri, "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", _package.Compression);
2011                        var rel = Part.CreateRelationship(PackUriHelper.GetRelativeUri(WorksheetUri, _comments.Uri), TargetMode.Internal, ExcelPackage.schemaRelationships+"/comments");
2012                    }
2013                    _comments.CommentXml.Save(_comments.Part.GetStream());
2014                }
2015            }
2016
2017            if (_vmlDrawings != null)
2018            {
2019                if (_vmlDrawings.Count == 0)
2020                {
2021                    if (_vmlDrawings.Uri != null)
2022                    {
2023                        Part.DeleteRelationship(_vmlDrawings.RelId);
2024                        _package.Package.DeletePart(_vmlDrawings.Uri);
2025                    }
2026                }
2027                else
2028                {
2029                    if (_vmlDrawings.Uri == null)
2030                    {
2031                        _vmlDrawings.Uri = XmlHelper.GetNewUri(_package.Package, @"/xl/drawings/vmlDrawing{0}.vml");
2032                    }
2033                    if (_vmlDrawings.Part == null)
2034                    {
2035                        _vmlDrawings.Part = _package.Package.CreatePart(_vmlDrawings.Uri, "application/vnd.openxmlformats-officedocument.vmlDrawing", _package.Compression);
2036                        var rel = Part.CreateRelationship(PackUriHelper.GetRelativeUri(WorksheetUri, _vmlDrawings.Uri), TargetMode.Internal, ExcelPackage.schemaRelationships + "/vmlDrawing");
2037                        SetXmlNodeString("d:legacyDrawing/@r:id", rel.Id);
2038                        _vmlDrawings.RelId = rel.Id;
2039                    }
2040                    _vmlDrawings.VmlDrawingXml.Save(_vmlDrawings.Part.GetStream());
2041                }
2042            }
2043        }
2044        /// <summary>
2045        /// Save all table data
2046        /// </summary>
2047        private void SaveTables()
2048        {
2049            foreach (var tbl in Tables)
2050            {
2051                if (tbl.ShowHeader || tbl.ShowTotal)
2052                {
2053                    int colNum = tbl.Address._fromCol;
2054                    foreach (var col in tbl.Columns)
2055                    {
2056                        if (tbl.ShowHeader)
2057                        {
2058                            Cell(tbl.Address._fromRow, colNum).Value = col.Name;
2059                        }
2060                        if (tbl.ShowTotal)
2061                        {
2062                            if (col.TotalsRowFunction == RowFunctions.Custom)
2063                            {
2064                                Cell(tbl.Address._toRow, colNum).Formula = col.TotalsRowFormula;
2065                            }
2066                            else if (col.TotalsRowFunction != RowFunctions.None)
2067                            {
2068                                switch (col.TotalsRowFunction)
2069                                {
2070                                    case RowFunctions.Average:
2071                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "101");
2072                                        break;
2073                                    case RowFunctions.Count:
2074                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "102");
2075                                        break;
2076                                    case RowFunctions.CountNums:
2077                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "103");
2078                                        break;
2079                                    case RowFunctions.Max:
2080                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "104");
2081                                        break;
2082                                    case RowFunctions.Min:
2083                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "105");
2084                                        break;
2085                                    case RowFunctions.StdDev:
2086                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "107");
2087                                        break;
2088                                    case RowFunctions.Var:
2089                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "110");
2090                                        break;
2091                                    case RowFunctions.Sum:
2092                                        Cell(tbl.Address._toRow, colNum).Formula = GetTotalFunction(col, "109");
2093                                        break;
2094                                    default:
2095                                        throw (new Exception("Unknown RowFunction enum"));
2096                                }
2097                            }
2098                            else
2099                            {
2100                                Cell(tbl.Address._toRow, colNum).Value = col.TotalsRowLabel;
2101                            }
2102                        }
2103                        if (!string.IsNullOrEmpty(col.CalculatedColumnFormula))
2104                        {
2105                            int fromRow = tbl.ShowHeader ? tbl.Address._fromRow + 1 : tbl.Address._fromRow;
2106                            int toRow = tbl.ShowTotal ? tbl.Address._toRow - 1 : tbl.Address._toRow;
2107                            for (int row = fromRow; row <= toRow; row++)
2108                            {
2109                                Cell(row, colNum).Formula = col.CalculatedColumnFormula;
2110                            }                           
2111                        }
2112                        colNum++;
2113                    }
2114                }               
2115                if (tbl.Part == null)
2116                {
2117                    tbl.TableUri = GetNewUri(_package.Package, @"/xl/tables/table{0}.xml", tbl.Id);
2118                    tbl.Part = _package.Package.CreatePart(tbl.TableUri, "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", Workbook._package.Compression);
2119                    var stream = tbl.Part.GetStream(FileMode.Create);
2120                    tbl.TableXml.Save(stream);
2121                    var rel = Part.CreateRelationship(PackUriHelper.GetRelativeUri(WorksheetUri, tbl.TableUri), TargetMode.Internal, ExcelPackage.schemaRelationships + "/table");
2122                    tbl.RelationshipID = rel.Id;
2123
2124                    CreateNode("d:tableParts");
2125                    XmlNode tbls = TopNode.SelectSingleNode("d:tableParts",NameSpaceManager);
2126
2127                    var tblNode = tbls.OwnerDocument.CreateElement("tablePart",ExcelPackage.schemaMain);
2128                    tbls.AppendChild(tblNode);
2129                    tblNode.SetAttribute("id",ExcelPackage.schemaRelationships, rel.Id);
2130                }
2131                else
2132                {
2133                    var stream = tbl.Part.GetStream(FileMode.Create);
2134                    tbl.TableXml.Save(stream);
2135                }
2136            }
2137        }
2138        private void SavePivotTables()
2139        {
2140            foreach (var pt in PivotTables)
2141            {
2142                if (pt.DataFields.Count > 1)
2143                {
2144                    XmlElement parentNode;
2145                    if(pt.DataOnRows==true)
2146                    {
2147                        parentNode =  pt.PivotTableXml.SelectSingleNode("//d:rowFields", pt.NameSpaceManager) as XmlElement;
2148                        if (parentNode == null)
2149                        {
2150                            pt.CreateNode("d:rowFields");
2151                            parentNode = pt.PivotTableXml.SelectSingleNode("//d:rowFields", pt.NameSpaceManager) as XmlElement;
2152                        }
2153                    }
2154                    else
2155                    {
2156                        parentNode =  pt.PivotTableXml.SelectSingleNode("//d:colFields", pt.NameSpaceManager) as XmlElement;
2157                        if (parentNode == null)
2158                        {
2159                            pt.CreateNode("d:colFields");
2160                            parentNode = pt.PivotTableXml.SelectSingleNode("//d:colFields", pt.NameSpaceManager) as XmlElement;
2161                        }
2162                    }
2163
2164                    if (parentNode.SelectSingleNode("d:field[@ x= \"-2\"]", pt.NameSpaceManager) == null)
2165                    {
2166                        XmlElement fieldNode = pt.PivotTableXml.CreateElement("field", ExcelPackage.schemaMain);
2167                        fieldNode.SetAttribute("x", "-2");
2168                        parentNode.AppendChild(fieldNode);
2169                    }
2170                }
2171                pt.PivotTableXml.Save(pt.Part.GetStream(FileMode.Create));
2172                pt.CacheDefinition.CacheDefinitionXml.Save(pt.CacheDefinition.Part.GetStream(FileMode.Create));
2173            }
2174        }
2175        private static string GetTotalFunction(ExcelTableColumn col,string FunctionNum)
2176        {
2177            return string.Format("SUBTOTAL({0},[{1}])", FunctionNum, col.Name);
2178        }
2179        private void SaveXml()
2180        {
2181            //Create the nodes if they do not exist.
2182            CreateNode("d:cols");
2183            CreateNode("d:sheetData");
2184            CreateNode("d:mergeCells");
2185            CreateNode("d:hyperlinks");
2186            CreateNode("d:rowBreaks");
2187            CreateNode("d:colBreaks");
2188
2189            string xml = _worksheetXml.OuterXml;
2190            StreamWriter sw=new StreamWriter(Part.GetStream(FileMode.Create, FileAccess.Write));
2191            int colStart = 0, colEnd = 0;
2192            GetBlockPos(xml, "cols", ref colStart, ref colEnd);
2193
2194            sw.Write(xml.Substring(0, colStart));
2195            var colBreaks = new List<int>();
2196            if (_columns.Count > 0)
2197            {
2198                UpdateColumnData(sw);
2199            }
2200
2201            int cellStart = colEnd, cellEnd = colEnd;
2202            GetBlockPos(xml, "sheetData", ref cellStart, ref cellEnd);
2203
2204            sw.Write(xml.Substring(colEnd, cellStart - colEnd));
2205            var rowBreaks = new List<int>();
2206            UpdateRowCellData(sw);
2207
2208            int mergeStart = cellEnd, mergeEnd = cellEnd;
2209
2210            GetBlockPos(xml, "mergeCells", ref mergeStart, ref mergeEnd);
2211            sw.Write(xml.Substring(cellEnd, mergeStart - cellEnd));
2212
2213            if (_mergedCells.Count > 0)
2214            {
2215                UpdateMergedCells(sw);
2216            }
2217
2218            int hyperStart = mergeEnd, hyperEnd = mergeEnd;
2219            GetBlockPos(xml, "hyperlinks", ref hyperStart, ref hyperEnd);
2220            sw.Write(xml.Substring(mergeEnd, hyperStart - mergeEnd));
2221            if (_hyperLinkCells.Count > 0)
2222            {
2223                UpdateHyperLinks(sw);
2224            }
2225
2226            int rowBreakStart = hyperEnd, rowBreakEnd = hyperEnd;
2227            GetBlockPos(xml, "rowBreaks", ref rowBreakStart, ref rowBreakEnd);
2228            sw.Write(xml.Substring(hyperEnd, rowBreakStart - hyperEnd));
2229            //if (rowBreaks.Count > 0)
2230            //{
2231            UpdateRowBreaks(sw);
2232            //}
2233
2234            int colBreakStart = rowBreakEnd, colBreakEnd = rowBreakEnd;
2235            GetBlockPos(xml, "colBreaks", ref colBreakStart, ref colBreakEnd);
2236            sw.Write(xml.Substring(rowBreakEnd, colBreakStart - rowBreakEnd));
2237            //if (colBreaks.Count > 0)
2238            //{
2239            UpdateColBreaks(sw);
2240            //}
2241
2242            sw.Write(xml.Substring(colBreakEnd, xml.Length - colBreakEnd));
2243            sw.Flush();
2244            sw.Close();
2245        }
2246        private void UpdateColBreaks(StreamWriter sw)
2247        {
2248            StringBuilder breaks = new StringBuilder();
2249            int count = 0;
2250            foreach (ExcelColumn col in _columns)
2251            {
2252                if (col.PageBreak)
2253                {
2254                    breaks.AppendFormat("<brk id=\"{0}\" max=\"16383\" man=\"1\" />", col.ColumnMin);
2255                    count++;
2256                }
2257            }
2258            if (count > 0)
2259            {
2260                sw.Write(string.Format("<colBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</colBreaks>", count, breaks.ToString()));
2261            }
2262        }
2263
2264        private void UpdateRowBreaks(StreamWriter sw)
2265        {
2266            StringBuilder breaks=new StringBuilder();
2267            int count = 0;
2268            foreach(ExcelRow row in _rows)           
2269            {
2270                if (row.PageBreak)
2271                {
2272                    breaks.AppendFormat("<brk id=\"{0}\" max=\"1048575\" man=\"1\" />", row.Row);
2273                    count++;
2274                }
2275            }
2276            if (count>0)
2277            {
2278                sw.Write(string.Format("<rowBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</rowBreaks>", count, breaks.ToString()));               
2279            }
2280        }
2281        /// <summary>
2282        /// Inserts the cols collection into the XML document
2283        /// </summary>
2284        private void UpdateColumnData(StreamWriter sw)
2285        {
2286            //ExcelColumn prevCol = null;   //commented out 11/1-12 JK
2287            //foreach (ExcelColumn col in _columns)
2288            //{               
2289            //    if (prevCol != null)
2290            //    {
2291            //        if(prevCol.ColumnMax != col.ColumnMin-1)
2292            //        {
2293            //            prevCol._columnMax=col.ColumnMin-1;
2294            //        }
2295            //    }
2296            //    prevCol = col;
2297            //}
2298            sw.Write("<cols>");
2299            foreach (ExcelColumn col in _columns)
2300            {
2301                ExcelStyleCollection<ExcelXfs> cellXfs = _package.Workbook.Styles.CellXfs;
2302
2303                sw.Write("<col min=\"{0}\" max=\"{1}\"", col.ColumnMin, col.ColumnMax);
2304                if (col.Hidden == true)
2305                {
2306                    //sbXml.Append(" width=\"0\" hidden=\"1\" customWidth=\"1\"");
2307                    sw.Write(" hidden=\"1\"");
2308                }
2309                else if (col.BestFit)
2310                {
2311                    sw.Write(" bestFit=\"1\"");
2312                }
2313                sw.Write(string.Format(CultureInfo.InvariantCulture, " width=\"{0}\" customWidth=\"1\"", col.Width));
2314                if (col.OutlineLevel > 0)
2315                {                   
2316                    sw.Write(" outlineLevel=\"{0}\" ", col.OutlineLevel);
2317                    if (col.Collapsed)
2318                    {
2319                        if (col.Hidden)
2320                        {
2321                            sw.Write(" collapsed=\"1\"");
2322                        }
2323                        else
2324                        {
2325                            sw.Write(" collapsed=\"1\" hidden=\"1\""); //Always hidden
2326                        }
2327                    }
2328                }
2329                if (col.Phonetic)
2330                {
2331                    sw.Write(" phonetic=\"1\"");
2332                }
2333                long styleID = col.StyleID >= 0 ? cellXfs[col.StyleID].newID : col.StyleID;
2334                if (styleID > 0)
2335                {
2336                    sw.Write(" style=\"{0}\"", styleID);
2337                }
2338                sw.Write(" />");
2339
2340                //if (col.PageBreak)
2341                //{
2342                //    colBreaks.Add(col.ColumnMin);
2343                //}
2344            }
2345            sw.Write("</cols>");
2346        }
2347        /// <summary>
2348        /// Insert row and cells into the XML document
2349        /// </summary>
2350        private void UpdateRowCellData(StreamWriter sw)
2351        {
2352            ExcelStyleCollection<ExcelXfs> cellXfs = _package.Workbook.Styles.CellXfs;
2353           
2354            _hyperLinkCells = new List<ulong>();
2355            int row = -1;
2356
2357            foreach (ExcelRow r in _rows)
2358            {
2359                int nextCell = ~_cells.IndexOf(r.RowID);
2360                if (nextCell >= 0 && (nextCell >= _cells.Count || ((ExcelCell)_cells[nextCell]).Row != r.Row))
2361                {
2362                    _cells.Add(r);
2363                }
2364            }
2365
2366            StringBuilder sbXml = new StringBuilder();
2367            var ss = _package.Workbook._sharedStrings;
2368            sw.Write("<sheetData>");
2369            foreach (IRangeID r in _cells)
2370            {
2371                if (r is ExcelCell)
2372                {
2373                    ExcelCell cell = (ExcelCell)r;
2374                    long styleID = cell.StyleID >= 0 ? cellXfs[cell.StyleID].newID : cell.StyleID;
2375
2376                    //Add the row element if it's a new row
2377                    if (row != cell.Row)
2378                    {
2379                        WriteRow(sw, cellXfs, row, cell.Row);
2380                        row = cell.Row;
2381                    }
2382                    if (cell.SharedFormulaID >= 0)
2383                    {
2384                        var f = _sharedFormulas[cell.SharedFormulaID];
2385                        if (f.Address.IndexOf(':') > 0)
2386                        {
2387                            if (f.StartCol == cell.Column && f.StartRow == cell.Row)
2388                            {
2389                                if (f.IsArray)
2390                                {
2391                                    sw.Write("<c r=\"{0}\" s=\"{1}\"><f ref=\"{2}\" t=\"array\">{3}</f></c>", cell.CellAddress, styleID < 0 ? 0 : styleID, f.Address, SecurityElement.Escape(f.Formula));
2392                                }
2393                                else
2394                                {
2395                                    sw.Write("<c r=\"{0}\" s=\"{1}\"><f ref=\"{2}\" t=\"shared\"  si=\"{3}\">{4}</f></c>", cell.CellAddress, styleID < 0 ? 0 : styleID, f.Address, cell.SharedFormulaID, SecurityElement.Escape(f.Formula));
2396                                }
2397
2398                            }
2399                            else if (f.IsArray)
2400                            {
2401                                sw.Write("<c r=\"{0}\" s=\"{1}\" />", cell.CellAddress, styleID < 0 ? 0 : styleID);
2402                            }
2403                            else
2404                            {
2405                                sw.Write("<c r=\"{0}\" s=\"{1}\"><f t=\"shared\" si=\"{2}\" /></c>", cell.CellAddress, styleID < 0 ? 0 : styleID, cell.SharedFormulaID);
2406                            }
2407                        }
2408                        else
2409                        {
2410                            // We can also have a single cell array formula
2411                            if(f.IsArray)
2412                            {
2413                                sw.Write("<c r=\"{0}\" s=\"{1}\"><f ref=\"{2}\" t=\"array\">{3}</f></c>", cell.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), SecurityElement.Escape(f.Formula));
2414                            }
2415                            else
2416                            {
2417                                sw.Write("<c r=\"{0}\" s=\"{1}\">", f.Address, styleID < 0 ? 0 : styleID);
2418                                sw.Write("<f>{0}</f></c>", SecurityElement.Escape(f.Formula));
2419                            }
2420                        }
2421                    }
2422                    else if (cell.Formula != "")
2423                    {
2424                        sw.Write("<c r=\"{0}\" s=\"{1}\">", cell.CellAddress, styleID < 0 ? 0 : styleID);
2425                        sw.Write("<f>{0}</f></c>", SecurityElement.Escape(cell.Formula));
2426                    }
2427                    else
2428                    {
2429                        if (cell._value == null)
2430                        {
2431                            sw.Write("<c r=\"{0}\" s=\"{1}\" />", cell.CellAddress, styleID < 0 ? 0 : styleID);
2432                        }
2433                        else
2434                        {
2435                            if ((cell._value.GetType().IsPrimitive || cell._value is double || cell._value is decimal || cell._value is DateTime || cell._value is TimeSpan) && cell.DataType != "s")
2436                            {
2437                                string s;
2438                                try
2439                                {
2440                                    if (cell._value is DateTime)
2441                                    {
2442                                        s = ((DateTime)cell.Value).ToOADate().ToString(CultureInfo.InvariantCulture);
2443                                    }
2444                                    else if (cell._value is TimeSpan)
2445                                    {
2446                                        s = new DateTime(((TimeSpan)cell.Value).Ticks).ToOADate().ToString(CultureInfo.InvariantCulture); ;
2447                                    }
2448                                    else
2449                                    {
2450                                        if (cell._value is double && double.IsNaN((double)cell._value))
2451                                        {
2452                                            s = "0";
2453                                        }
2454                                        else if (cell._value is double && double.IsInfinity((double)cell._value))
2455                                        {
2456                                            s="#NUM!";
2457                                        }
2458                                        else
2459                                        {
2460                                            s = Convert.ToDouble(cell._value, CultureInfo.InvariantCulture).ToString("g15", CultureInfo.InvariantCulture);
2461                                        }
2462                                    }
2463                                }
2464
2465                                catch
2466                                {
2467                                    s = "0";
2468                                }
2469                                if (cell._value is bool)
2470                                {
2471                                    sw.Write("<c r=\"{0}\" s=\"{1}\" t=\"b\">", cell.CellAddress, styleID < 0 ? 0 : styleID);
2472                                }
2473                                else if (cell._value is double && double.IsInfinity((double)cell._value))
2474                                {
2475                                    sw.Write("<c r=\"{0}\" s=\"{1}\" t=\"e\">", cell.CellAddress, styleID < 0 ? 0 : styleID);
2476                                }
2477                                else
2478                                {
2479                                    sw.Write("<c r=\"{0}\" s=\"{1}\">", cell.CellAddress, styleID < 0 ? 0 : styleID);
2480                                }
2481                                sw.Write("<v>{0}</v></c>", s);
2482                            }
2483                            else
2484                            {
2485                                int ix;
2486                                if (!ss.ContainsKey(cell._value.ToString()))
2487                                {
2488                                    ix = ss.Count;
2489                                    ss.Add(cell._value.ToString(), new ExcelWorkbook.SharedStringItem() { isRichText = cell.IsRichText, pos = ix });
2490                                }
2491                                else
2492                                {
2493                                    ix = ss[cell.Value.ToString()].pos;
2494                                }
2495                                sw.Write("<c r=\"{0}\" s=\"{1}\" t=\"s\">", cell.CellAddress, styleID < 0 ? 0 : styleID);
2496                                sw.Write("<v>{0}</v></c>", ix);
2497                            }
2498                        }
2499                    }
2500                    //Update hyperlinks.
2501                    if (cell.Hyperlink != null)
2502                    {
2503                        _hyperLinkCells.Add(cell.CellID);
2504                    }
2505                }
2506                else  //ExcelRow
2507                {
2508                    int newRow=((ExcelRow)r).Row;
2509                    WriteRow(sw, cellXfs, row, newRow);
2510                    row = newRow;
2511                }
2512            }
2513
2514            if (row != -1) sw.Write("</row>");
2515            sw.Write("</sheetData>");
2516
2517           
2518        }
2519
2520        private void WriteRow(StreamWriter sw, ExcelStyleCollection<ExcelXfs> cellXfs, int prevRow, int row)
2521        {
2522            if (prevRow != -1) sw.Write("</row>");
2523            ulong rowID = ExcelRow.GetRowID(SheetID, row);
2524            sw.Write("<row r=\"{0}\" ", row);
2525            if (_rows.ContainsKey(rowID))
2526            {
2527                ExcelRow currRow = _rows[rowID] as ExcelRow;
2528                if (currRow.Hidden == true)
2529                {
2530                    sw.Write("ht=\"0\" hidden=\"1\" ");
2531                }
2532                else if (currRow.Height != DefaultRowHeight)
2533                {
2534                    sw.Write(string.Format(CultureInfo.InvariantCulture, "ht=\"{0}\" ", currRow.Height));
2535                    if (currRow.CustomHeight)
2536                    {
2537                        sw.Write("customHeight=\"1\" ");
2538                    }
2539                }
2540
2541                if (currRow.StyleID > 0)
2542                {
2543                    sw.Write("s=\"{0}\" customFormat=\"1\" ", cellXfs[currRow.StyleID].newID);
2544                }
2545                if (currRow.OutlineLevel > 0)
2546                {
2547                    sw.Write("outlineLevel =\"{0}\" ", currRow.OutlineLevel);
2548                    if (currRow.Collapsed)
2549                    {
2550                        if (currRow.Hidden)
2551                        {
2552                            sw.Write(" collapsed=\"1\"");
2553                        }
2554                        else
2555                        {
2556                            sw.Write(" collapsed=\"1\" hidden=\"1\""); //Always hidden
2557                        }
2558                    }
2559                }
2560                if (currRow.Phonetic)
2561                {
2562                    sw.Write("ph=\"1\" ");
2563                }
2564            }
2565            sw.Write(">");
2566        }
2567
2568        /// <summary>
2569        /// Update xml with hyperlinks
2570        /// </summary>
2571        /// <param name="sw">The stream</param>
2572        private void UpdateHyperLinks(StreamWriter sw)
2573        {
2574                sw.Write("<hyperlinks>");
2575                Dictionary<string, string> hyps = new Dictionary<string, string>();
2576                foreach (ulong cellId in _hyperLinkCells)
2577                {
2578                    ExcelCell cell = _cells[cellId] as ExcelCell;
2579                    if (cell.Hyperlink is ExcelHyperLink && !string.IsNullOrEmpty((cell.Hyperlink as ExcelHyperLink).ReferenceAddress))
2580                    {
2581                        ExcelHyperLink hl = cell.Hyperlink as ExcelHyperLink;
2582                        sw.Write("<hyperlink ref=\"{0}\" location=\"{1}\" {2}{3}/>",
2583                                Cells[cell.Row, cell.Column, cell.Row+hl.RowSpann, cell.Column+hl.ColSpann].Address,
2584                                ExcelCell.GetFullAddress(Name, hl.ReferenceAddress),
2585                                    string.IsNullOrEmpty(hl.Display) ? "" : "display=\"" + SecurityElement.Escape(hl.Display) + "\" ",
2586                                    string.IsNullOrEmpty(hl.ToolTip) ? "" : "tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\" ");
2587                    }
2588                    else
2589                    {
2590                        string id;
2591                        Uri hyp;
2592                        if (cell.Hyperlink is ExcelHyperLink)
2593                        {
2594                            hyp = ((ExcelHyperLink)cell.Hyperlink).OriginalUri;
2595                        }
2596                        else
2597                        {
2598                            hyp = cell.Hyperlink;
2599                        }
2600                        if (hyps.ContainsKey(hyp.OriginalString))
2601                        {
2602                            id = hyps[hyp.OriginalString];
2603                        }
2604                        else
2605                        {
2606                            PackageRelationship relationship = Part.CreateRelationship(hyp, TargetMode.External, ExcelPackage.schemaHyperlink);
2607                            if (cell.Hyperlink is ExcelHyperLink)
2608                            {
2609                                ExcelHyperLink hl = cell.Hyperlink as ExcelHyperLink;
2610                                sw.Write("<hyperlink ref=\"{0}\" {2}{3}r:id=\"{1}\" />", cell.CellAddress, relationship.Id,                               
2611                                    string.IsNullOrEmpty(hl.Display) ? "" : "display=\"" + SecurityElement.Escape(hl.Display) + "\" ",
2612                                    string.IsNullOrEmpty(hl.ToolTip) ? "" : "tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\" ");
2613                            }
2614                            else
2615                            {
2616                                sw.Write("<hyperlink ref=\"{0}\" r:id=\"{1}\" />",cell.CellAddress, relationship.Id);
2617                            }
2618                            id = relationship.Id;
2619                        }
2620                        cell.HyperLinkRId = id;
2621                    }
2622                }
2623                sw.Write("</hyperlinks>");
2624        }
2625        /// <summary>
2626        /// Create the hyperlinks node in the XML
2627        /// </summary>
2628        /// <returns></returns>
2629        private XmlNode CreateHyperLinkCollection()
2630        {
2631            XmlElement hl=_worksheetXml.CreateElement("hyperlinks",ExcelPackage.schemaMain);
2632            XmlNode prevNode = _worksheetXml.SelectSingleNode("//d:conditionalFormatting", NameSpaceManager);
2633            if (prevNode == null)
2634            {
2635                prevNode = _worksheetXml.SelectSingleNode("//d:mergeCells", NameSpaceManager);
2636                if (prevNode == null)
2637                {
2638                    prevNode = _worksheetXml.SelectSingleNode("//d:sheetData", NameSpaceManager);
2639                }
2640            }
2641            return _worksheetXml.DocumentElement.InsertAfter(hl, prevNode);
2642        }
2643        /// <summary>
2644        /// Dimension address for the worksheet.
2645        /// Top left cell to Bottom right.
2646        /// If the worksheet has no cells, null is returned
2647        /// </summary>
2648        public ExcelAddressBase Dimension
2649        {
2650            get
2651            {
2652                if (_cells.Count > 0)
2653                {
2654                    ExcelAddressBase addr = new ExcelAddressBase((_cells[0] as ExcelCell).Row, _minCol, (_cells[_cells.Count - 1] as ExcelCell).Row, _maxCol);
2655                    addr._ws = Name;
2656                    return addr;
2657                }
2658                else
2659                {
2660                    return null;
2661                }
2662            }
2663        }
2664        ExcelSheetProtection _protection=null;
2665        /// <summary>
2666        /// Access to sheet protection properties
2667        /// </summary>
2668        public ExcelSheetProtection Protection
2669        {
2670            get
2671            {
2672                if (_protection == null)
2673                {
2674                    _protection = new ExcelSheetProtection(NameSpaceManager, TopNode, this);
2675                }
2676                return _protection;
2677            }
2678        }
2679
2680        private ExcelProtectedRangeCollection _protectedRanges;
2681        public ExcelProtectedRangeCollection ProtectedRanges
2682        {
2683            get
2684            {
2685                if (_protectedRanges == null)
2686                    _protectedRanges = new ExcelProtectedRangeCollection(NameSpaceManager, TopNode, this);
2687                return _protectedRanges;
2688            }
2689        }
2690
2691        #region Drawing
2692        ExcelDrawings _drawings = null;
2693        /// <summary>
2694        /// Collection of drawing-objects like shapes, images and charts
2695        /// </summary>
2696        public ExcelDrawings Drawings
2697        {
2698            get
2699            {
2700                if (_drawings == null)
2701                {
2702                    _drawings = new ExcelDrawings(_package, this);
2703                }
2704                return _drawings;
2705            }
2706        }
2707        #endregion
2708        ExcelTableCollection _tables = null;
2709        /// <summary>
2710        /// Tables defined in the worksheet.
2711        /// </summary>
2712        public ExcelTableCollection Tables
2713        {
2714            get
2715            {
2716                if (_tables == null)
2717                {
2718                    _tables = new ExcelTableCollection(this);
2719                }
2720                return _tables;
2721            }
2722        }
2723        ExcelPivotTableCollection _pivotTables = null;
2724        /// <summary>
2725        /// Pivottables defined in the worksheet.
2726        /// </summary>
2727        public ExcelPivotTableCollection PivotTables
2728        {
2729            get
2730            {
2731                if (_pivotTables == null)
2732                {
2733                    _pivotTables = new ExcelPivotTableCollection(this);
2734                }
2735                return _pivotTables;
2736            }
2737        }
2738        private ExcelConditionalFormattingCollection _conditionalFormatting = null;
2739        /// <summary>
2740        /// ConditionalFormatting defined in the worksheet. Use the Add methods to create ConditionalFormatting and add them to the worksheet. Then
2741        /// set the properties on the instance returned.
2742        /// </summary>
2743        /// <seealso cref="ExcelConditionalFormattingCollection"/>
2744        public ExcelConditionalFormattingCollection ConditionalFormatting
2745        {
2746            get
2747            {
2748                if (_conditionalFormatting == null)
2749                {
2750                    _conditionalFormatting = new ExcelConditionalFormattingCollection(this);
2751                }
2752                return _conditionalFormatting;
2753            }
2754        }
2755        private ExcelDataValidationCollection _dataValidation = null;
2756        /// <summary>
2757        /// DataValidation defined in the worksheet. Use the Add methods to create DataValidations and add them to the worksheet. Then
2758        /// set the properties on the instance returned.
2759        /// </summary>
2760        /// <seealso cref="ExcelDataValidationCollection"/>
2761        public ExcelDataValidationCollection DataValidations
2762        {
2763            get
2764            {
2765                if (_dataValidation == null)
2766                {
2767                    _dataValidation = new ExcelDataValidationCollection(this);
2768                }
2769                return _dataValidation;
2770            }
2771        }
2772        ExcelBackgroundImage _backgroundImage = null;
2773        /// <summary>
2774        /// An image displayed as the background of the worksheet.
2775        /// </summary>
2776        public ExcelBackgroundImage BackgroundImage
2777        {
2778            get
2779            {
2780                if (_backgroundImage == null)
2781                {
2782                    _backgroundImage = new ExcelBackgroundImage(NameSpaceManager, TopNode, this);
2783                }
2784                return _backgroundImage;
2785            }
2786        }
2787        /// <summary>
2788    /// Returns the style ID given a style name. 
2789    /// The style ID will be created if not found, but only if the style name exists!
2790    /// </summary>
2791    /// <param name="StyleName"></param>
2792    /// <returns></returns>
2793    internal int GetStyleID(string StyleName)
2794    {
2795      ExcelNamedStyleXml namedStyle=null;
2796            Workbook.Styles.NamedStyles.FindByID(StyleName, ref namedStyle);
2797            if (namedStyle.XfId == int.MinValue)
2798            {
2799                namedStyle.XfId=Workbook.Styles.CellXfs.FindIndexByID(namedStyle.Style.Id);
2800            }
2801            return namedStyle.XfId;
2802    }
2803        /// <summary>
2804        /// The workbook object
2805        /// </summary>
2806        public ExcelWorkbook Workbook
2807        {
2808            get
2809            {
2810                return _package.Workbook;
2811            }
2812        }
2813    #endregion
2814        #endregion  // END Worksheet Private Methods
2815
2816        /// <summary>
2817        /// Get the next ID from a shared formula or an Array formula
2818        /// Sharedforumlas will have an id from 0-x. Array formula ids start from 0x4000001-.
2819        /// </summary>
2820        /// <param name="isArray">If the formula is an array formula</param>
2821        /// <returns></returns>
2822        internal int GetMaxShareFunctionIndex(bool isArray)
2823        {
2824            int i=_sharedFormulas.Count + 1;
2825            if (isArray)
2826                i |= 0x40000000;
2827
2828            while(_sharedFormulas.ContainsKey(i))
2829            {
2830                i++;
2831            }
2832            return i;
2833        }
2834        internal void SetHFLegacyDrawingRel(string relID)
2835        {
2836            SetXmlNodeString("d:legacyDrawingHF/@r:id", relID);
2837        }
2838        internal void RemoveLegacyDrawingRel(string relID)
2839        {
2840            var n = WorksheetXml.DocumentElement.SelectSingleNode(string.Format("d:legacyDrawing[@r:id=\"{0}\"]", relID), NameSpaceManager);
2841            if (n != null)
2842            {
2843                n.ParentNode.RemoveChild(n);
2844            }
2845        }
2846    }  // END class Worksheet
2847}
Note: See TracBrowser for help on using the repository browser.