Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/ExcelWorkbook.cs @ 16141

Last change on this file since 16141 was 12463, checked in by ascheibe, 9 years ago

#2399 fixed EPPlus so that it compiles on Linux

File size: 37.3 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-01-01
30 * Jan Källman        License changed GPL-->LGPL 2011-12-27
31 * Richard Tallent    Fix escaping of quotes          2012-10-31
32 *******************************************************************************/
33using System;
34using System.Xml;
35using System.IO;
36using System.Collections.Generic;
37using System.Text;
38using System.Security;
39using System.Globalization;
40using System.Text.RegularExpressions;
41using OfficeOpenXml.VBA;
42using System.Drawing;
43using OfficeOpenXml.Utils;
44using System.Windows.Media;
45using System.Windows;
46using Ionic.Zip;
47using OfficeOpenXml.FormulaParsing;
48using OfficeOpenXml.FormulaParsing.Excel.Functions;
49using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
50using OfficeOpenXml.Packaging.Ionic.Zip;
51namespace OfficeOpenXml
52{
53  #region Public Enum ExcelCalcMode
54  /// <summary>
55  /// How the application should calculate formulas in the workbook
56  /// </summary>
57  public enum ExcelCalcMode
58  {
59    /// <summary>
60    /// Indicates that calculations in the workbook are performed automatically when cell values change.
61    /// The application recalculates those cells that are dependent on other cells that contain changed values.
62    /// This mode of calculation helps to avoid unnecessary calculations.
63    /// </summary>
64    Automatic,
65    /// <summary>
66    /// Indicates tables be excluded during automatic calculation
67    /// </summary>
68    AutomaticNoTable,
69    /// <summary>
70    /// Indicates that calculations in the workbook be triggered manually by the user.
71    /// </summary>
72    Manual
73  }
74  #endregion
75
76  /// <summary>
77  /// Represents the Excel workbook and provides access to all the
78  /// document properties and worksheets within the workbook.
79  /// </summary>
80  public sealed class ExcelWorkbook : XmlHelper, IDisposable
81  {
82    internal class SharedStringItem
83    {
84      internal int pos;
85      internal string Text;
86      internal bool isRichText = false;
87    }
88    #region Private Properties
89    internal ExcelPackage _package;
90    private ExcelWorksheets _worksheets;
91    private OfficeProperties _properties;
92
93    private ExcelStyles _styles;
94    #endregion
95
96    #region ExcelWorkbook Constructor
97    /// <summary>
98    /// Creates a new instance of the ExcelWorkbook class.
99    /// </summary>
100    /// <param name="package">The parent package</param>
101    /// <param name="namespaceManager">NamespaceManager</param>
102    internal ExcelWorkbook(ExcelPackage package, XmlNamespaceManager namespaceManager) :
103      base(namespaceManager)
104    {
105      _package = package;
106      WorkbookUri = new Uri("/xl/workbook.xml", UriKind.Relative);
107      SharedStringsUri = new Uri("/xl/sharedStrings.xml", UriKind.Relative);
108      StylesUri = new Uri("/xl/styles.xml", UriKind.Relative);
109
110      _names = new ExcelNamedRangeCollection(this);
111      _namespaceManager = namespaceManager;
112      TopNode = WorkbookXml.DocumentElement;
113      SchemaNodeOrder = new string[] { "fileVersion", "fileSharing", "workbookPr", "workbookProtection", "bookViews", "sheets", "functionGroups", "functionPrototypes", "externalReferences", "definedNames", "calcPr", "oleSize", "customWorkbookViews", "pivotCaches", "smartTagPr", "smartTagTypes", "webPublishing", "fileRecoveryPr", };
114        FullCalcOnLoad = true;  //Full calculation on load by default, for both new workbooks and templates.
115      GetSharedStrings();
116    }
117    #endregion
118
119    internal Dictionary<string, SharedStringItem> _sharedStrings = new Dictionary<string, SharedStringItem>(); //Used when reading cells.
120    internal List<SharedStringItem> _sharedStringsList = new List<SharedStringItem>(); //Used when reading cells.
121    internal ExcelNamedRangeCollection _names;
122    internal int _nextDrawingID = 0;
123    internal int _nextTableID = 1;
124    internal int _nextPivotTableID = 1;
125    internal XmlNamespaceManager _namespaceManager;
126        internal FormulaParser _formulaParser = null;
127      internal FormulaParserManager _parserManager;
128        internal CellStore<List<Token>> _formulaTokens;
129    /// <summary>
130    /// Read shared strings to list
131    /// </summary>
132    private void GetSharedStrings()
133    {
134      if (_package.Package.PartExists(SharedStringsUri))
135      {
136        var xml = _package.GetXmlFromUri(SharedStringsUri);
137        XmlNodeList nl = xml.SelectNodes("//d:sst/d:si", NameSpaceManager);
138        _sharedStringsList = new List<SharedStringItem>();
139        if (nl != null)
140        {
141          foreach (XmlNode node in nl)
142          {
143            XmlNode n = node.SelectSingleNode("d:t", NameSpaceManager);
144            if (n != null)
145            {
146                            _sharedStringsList.Add(new SharedStringItem() { Text = ConvertUtil.ExcelDecodeString(n.InnerText) });
147            }
148            else
149            {
150              _sharedStringsList.Add(new SharedStringItem() { Text = node.InnerXml, isRichText = true });
151            }
152          }
153        }
154                //Delete the shared string part, it will be recreated when the package is saved.
155                foreach (var rel in Part.GetRelationships())
156                {
157                    if (rel.TargetUri.OriginalString.EndsWith("sharedstrings.xml", StringComparison.InvariantCultureIgnoreCase))
158                    {
159                        Part.DeleteRelationship(rel.Id);
160                        break;
161                    }
162                }               
163                _package.Package.DeletePart(SharedStringsUri); //Remove the part, it is recreated when saved.
164      }
165    }
166    internal void GetDefinedNames()
167    {
168      XmlNodeList nl = WorkbookXml.SelectNodes("//d:definedNames/d:definedName", NameSpaceManager);
169      if (nl != null)
170      {
171        foreach (XmlElement elem in nl)
172        {
173          string fullAddress = elem.InnerText;
174
175          int localSheetID;
176          ExcelWorksheet nameWorksheet;
177          if(!int.TryParse(elem.GetAttribute("localSheetId"), out localSheetID))
178          {
179            localSheetID = -1;
180            nameWorksheet=null;
181          }
182          else
183          {
184            nameWorksheet=Worksheets[localSheetID + 1];
185          }
186          var addressType = ExcelAddressBase.IsValid(fullAddress);
187          ExcelRangeBase range;
188          ExcelNamedRange namedRange;
189
190          if (fullAddress.IndexOf("[") == 0)
191          {
192            int start = fullAddress.IndexOf("[");
193            int end = fullAddress.IndexOf("]", start);
194            if (start >= 0 && end >= 0)
195            {
196
197              string externalIndex = fullAddress.Substring(start + 1, end - start - 1);
198              int index;
199              if (int.TryParse(externalIndex, out index))
200              {
201                if (index > 0 && index <= _externalReferences.Count)
202                {
203                  fullAddress = fullAddress.Substring(0, start) + "[" + _externalReferences[index - 1] + "]" + fullAddress.Substring(end + 1);
204                }
205              }
206            }
207          }
208
209          if (addressType == ExcelAddressBase.AddressType.Invalid || addressType == ExcelAddressBase.AddressType.InternalName || addressType == ExcelAddressBase.AddressType.ExternalName || addressType==ExcelAddressBase.AddressType.Formula || addressType==ExcelAddressBase.AddressType.ExternalAddress)    //A value or a formula
210          {
211            double value;
212            range = new ExcelRangeBase(this, nameWorksheet, elem.GetAttribute("name"), true);
213            if (nameWorksheet == null)
214            {
215              namedRange = _names.Add(elem.GetAttribute("name"), range);
216            }
217            else
218            {
219              namedRange = nameWorksheet.Names.Add(elem.GetAttribute("name"), range);
220            }
221           
222            if (fullAddress.StartsWith("\"")) //String value
223            {
224              namedRange.NameValue = fullAddress.Substring(1,fullAddress.Length-2);
225            }
226            else if (double.TryParse(fullAddress, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
227            {
228              namedRange.NameValue = value;
229            }
230            else
231            {
232                            //if (addressType == ExcelAddressBase.AddressType.ExternalAddress || addressType == ExcelAddressBase.AddressType.ExternalName)
233                            //{
234                            //    var r = new ExcelAddress(fullAddress);
235                            //    namedRange.NameFormula = '\'[' + r._wb
236                            //}
237                            //else
238                            //{
239                                namedRange.NameFormula = fullAddress;
240                            //}
241            }
242          }
243          else
244          {
245            ExcelAddress addr = new ExcelAddress(fullAddress, _package, null);
246            if (localSheetID > -1)
247            {
248              if (string.IsNullOrEmpty(addr._ws))
249              {
250                namedRange = Worksheets[localSheetID + 1].Names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, Worksheets[localSheetID + 1], fullAddress, false));
251              }
252              else
253              {
254                namedRange = Worksheets[localSheetID + 1].Names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, Worksheets[addr._ws], fullAddress, false));
255              }
256            }
257            else
258            {
259              var ws = Worksheets[addr._ws];
260              namedRange = _names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, ws, fullAddress, false));
261            }
262          }
263          if (elem.GetAttribute("hidden") == "1" && namedRange != null) namedRange.IsNameHidden = true;
264          if(!string.IsNullOrEmpty(elem.GetAttribute("comment"))) namedRange.NameComment=elem.GetAttribute("comment");
265        }
266      }
267    }
268    #region Worksheets
269    /// <summary>
270    /// Provides access to all the worksheets in the workbook.
271    /// </summary>
272    public ExcelWorksheets Worksheets
273    {
274      get
275      {
276        if (_worksheets == null)
277        {
278          var sheetsNode = _workbookXml.DocumentElement.SelectSingleNode("d:sheets", _namespaceManager);
279          if (sheetsNode == null)
280          {
281            sheetsNode = CreateNode("d:sheets");
282          }
283         
284          _worksheets = new ExcelWorksheets(_package, _namespaceManager, sheetsNode);
285        }
286        return (_worksheets);
287      }
288    }
289    #endregion
290
291    /// <summary>
292    /// Provides access to named ranges
293    /// </summary>
294    public ExcelNamedRangeCollection Names
295    {
296      get
297      {
298        return _names;
299      }
300    }
301    #region Workbook Properties
302    decimal _standardFontWidth = decimal.MinValue;
303        string _fontID = "";
304        internal FormulaParser FormulaParser
305        {
306            get
307            {
308                if (_formulaParser == null)
309                {
310                    _formulaParser = new FormulaParser(new EpplusExcelDataProvider(_package));
311                }
312                return _formulaParser;
313            }
314        }
315
316      public FormulaParserManager FormulaParserManager
317      {
318          get
319          {
320              if (_parserManager == null)
321              {
322                  _parserManager = new FormulaParserManager(FormulaParser);
323              }
324              return _parserManager;
325          }
326      }
327
328        /// <summary>
329    /// Max font width for the workbook
330        /// <remarks>This method uses GDI. If you use Asure or another environment that does not support GDI, you have to set this value manually if you don't use the standard Calibri font</remarks>
331    /// </summary>
332    public decimal MaxFontWidth
333    {
334      get
335      {
336                if (_standardFontWidth == decimal.MinValue || _fontID != Styles.Fonts[0].Id)
337        {
338          var font = Styles.Fonts[0];
339#if __MonoCS__
340    _standardFontWidth = (int)(font.Size * (2D / 3D)); //Aprox for Calibri.
341#else
342                    try
343                    {
344                        //Font f = new Font(font.Name, font.Size);
345                        _standardFontWidth = 0;
346                        _fontID = font.Id;                       
347                        Typeface tf = new Typeface(new System.Windows.Media.FontFamily(font.Name),
348                                                     (font.Italic) ? FontStyles.Normal : FontStyles.Italic,
349                                                     (font.Bold) ? FontWeights.Bold : FontWeights.Normal,
350                                                     FontStretches.Normal);
351                        for(int i=0;i<10;i++)
352                        {
353                            var ft = new System.Windows.Media.FormattedText("0123456789".Substring(i,1), CultureInfo.InvariantCulture, System.Windows.FlowDirection.LeftToRight, tf, font.Size * (96D / 72D), System.Windows.Media.Brushes.Black);
354                            var width=(int)Math.Round(ft.Width,0);   
355                            if(width>_standardFontWidth)
356                            {
357                                _standardFontWidth = width;
358                            }
359                        }
360                         
361                        //var size = new System.Windows.Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
362
363                        //using (Bitmap b = new Bitmap(1, 1))
364                        //{
365                        //    using (Graphics g = Graphics.FromImage(b))
366                        //    {
367                        //        _standardFontWidth = (decimal)Math.Truncate(g.MeasureString("00", f).Width - g.MeasureString("0", f).Width);
368                        //    }
369                        //}
370                        if (_standardFontWidth <= 0) //No GDI?
371                        {
372                            _standardFontWidth = (int)(font.Size * (2D / 3D)); //Aprox. for Calibri.
373                        }
374                    }
375                    catch   //Error, set default value
376                    {
377                        _standardFontWidth = (int)(font.Size * (2D / 3D)); //Aprox for Calibri.
378                    }
379#endif
380        }
381        return _standardFontWidth;
382      }
383            set
384            {
385                _standardFontWidth = value;
386            }
387    }
388    ExcelProtection _protection = null;
389    /// <summary>
390    /// Access properties to protect or unprotect a workbook
391    /// </summary>
392    public ExcelProtection Protection
393    {
394      get
395      {
396        if (_protection == null)
397        {
398          _protection = new ExcelProtection(NameSpaceManager, TopNode, this);
399          _protection.SchemaNodeOrder = SchemaNodeOrder;
400        }
401        return _protection;
402      }
403    }       
404    ExcelWorkbookView _view = null;
405    /// <summary>
406    /// Access to workbook view properties
407    /// </summary>
408    public ExcelWorkbookView View
409    {
410      get
411      {
412        if (_view == null)
413        {
414          _view = new ExcelWorkbookView(NameSpaceManager, TopNode, this);
415        }
416        return _view;
417      }
418    }
419        ExcelVbaProject _vba = null;
420        /// <summary>
421        /// A reference to the VBA project.
422        /// Null if no project exists.
423        /// Use Workbook.CreateVBAProject to create a new VBA-Project
424        /// </summary>
425        public ExcelVbaProject VbaProject
426        {
427            get
428            {
429                if (_vba == null)
430                {
431                    if(_package.Package.PartExists(new Uri(ExcelVbaProject.PartUri, UriKind.Relative)))
432                    {
433                        _vba = new ExcelVbaProject(this);
434                    }
435                }
436                return _vba;
437            }
438        }
439
440        /// <summary>
441        /// Create an empty VBA project.
442        /// </summary>
443        public void CreateVBAProject()
444        {
445#if !MONO
446            if (_vba != null || _package.Package.PartExists(new Uri(ExcelVbaProject.PartUri, UriKind.Relative)))
447            {
448                throw (new InvalidOperationException("VBA project already exists."));
449            }
450                       
451            _vba = new ExcelVbaProject(this);
452            _vba.Create();
453#endif
454#if MONO
455            throw new NotSupportedException("Creating a VBA project is not supported under Mono.");
456#endif
457        }
458    /// <summary>
459    /// URI to the workbook inside the package
460    /// </summary>
461    internal Uri WorkbookUri { get; private set; }
462    /// <summary>
463        /// URI to the styles inside the package
464    /// </summary>
465    internal Uri StylesUri { get; private set; }
466    /// <summary>
467        /// URI to the shared strings inside the package
468    /// </summary>
469    internal Uri SharedStringsUri { get; private set; }
470    /// <summary>
471    /// Returns a reference to the workbook's part within the package
472    /// </summary>
473    internal Packaging.ZipPackagePart Part { get { return (_package.Package.GetPart(WorkbookUri)); } }
474   
475    #region WorkbookXml
476    private XmlDocument _workbookXml;
477    /// <summary>
478    /// Provides access to the XML data representing the workbook in the package.
479    /// </summary>
480    public XmlDocument WorkbookXml
481    {
482      get
483      {
484        if (_workbookXml == null)
485        {
486          CreateWorkbookXml(_namespaceManager);
487        }
488        return (_workbookXml);
489      }
490    }
491        const string codeModuleNamePath = "d:workbookPr/@codeName";
492        internal string CodeModuleName
493        {
494            get
495            {
496                return GetXmlNodeString(codeModuleNamePath);
497            }
498            set
499            {
500                SetXmlNodeString(codeModuleNamePath,value);
501            }
502        }
503        internal void CodeNameChange(string value)
504        {
505            CodeModuleName = value;
506        }
507        public VBA.ExcelVBAModule CodeModule
508        {
509            get
510            {
511                if (VbaProject != null)
512                {
513                    return VbaProject.Modules[CodeModuleName];
514                }
515                else
516                {
517                    return null;
518                }
519            }
520        }
521
522        const string date1904Path = "d:workbookPr/@date1904";
523        internal const double date1904Offset = 365.5 * 4;  // offset to fix 1900 and 1904 differences, 4 OLE years
524        /// <summary>
525        /// The date systems used by Microsoft Excel can be based on one of two different dates. By default, a serial number of 1 in Microsoft Excel represents January 1, 1900.
526        /// The default for the serial number 1 can be changed to represent January 2, 1904.
527        /// This option was included in Microsoft Excel for Windows to make it compatible with Excel for the Macintosh, which defaults to January 2, 1904.
528        /// </summary>
529        public bool Date1904
530        {
531            get
532            {
533                return GetXmlNodeBool(date1904Path, false);
534               
535            }
536            set
537            {
538                if (Date1904 != value)
539                {
540                    // Like Excel when the option it's changed update it all cells with Date format
541                    foreach (var item in Worksheets)
542                    {
543                        item.UpdateCellsWithDate1904Setting();
544                    }
545                }
546
547                SetXmlNodeBool(date1904Path, value, false);
548            }
549        }
550     
551       
552    /// <summary>
553    /// Create or read the XML for the workbook.
554    /// </summary>
555    private void CreateWorkbookXml(XmlNamespaceManager namespaceManager)
556    {
557      if (_package.Package.PartExists(WorkbookUri))
558        _workbookXml = _package.GetXmlFromUri(WorkbookUri);
559      else
560      {
561        // create a new workbook part and add to the package
562        Packaging.ZipPackagePart partWorkbook = _package.Package.CreatePart(WorkbookUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", _package.Compression);
563
564        // create the workbook
565        _workbookXml = new XmlDocument(namespaceManager.NameTable);               
566       
567        _workbookXml.PreserveWhitespace = ExcelPackage.preserveWhitespace;
568        // create the workbook element
569        XmlElement wbElem = _workbookXml.CreateElement("workbook", ExcelPackage.schemaMain);
570
571        // Add the relationships namespace
572        wbElem.SetAttribute("xmlns:r", ExcelPackage.schemaRelationships);
573
574        _workbookXml.AppendChild(wbElem);
575
576        // create the bookViews and workbooks element
577        XmlElement bookViews = _workbookXml.CreateElement("bookViews", ExcelPackage.schemaMain);
578        wbElem.AppendChild(bookViews);
579        XmlElement workbookView = _workbookXml.CreateElement("workbookView", ExcelPackage.schemaMain);
580        bookViews.AppendChild(workbookView);
581
582        // save it to the package
583        StreamWriter stream = new StreamWriter(partWorkbook.GetStream(FileMode.Create, FileAccess.Write));
584        _workbookXml.Save(stream);
585        //stream.Close();
586        _package.Package.Flush();
587      }
588    }
589    #endregion
590    #region StylesXml
591    private XmlDocument _stylesXml;
592    /// <summary>
593    /// Provides access to the XML data representing the styles in the package.
594    /// </summary>
595    public XmlDocument StylesXml
596    {
597      get
598      {
599        if (_stylesXml == null)
600        {
601          if (_package.Package.PartExists(StylesUri))
602            _stylesXml = _package.GetXmlFromUri(StylesUri);
603          else
604          {
605            // create a new styles part and add to the package
606            Packaging.ZipPackagePart part = _package.Package.CreatePart(StylesUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", _package.Compression);
607            // create the style sheet
608
609            StringBuilder xml = new StringBuilder("<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
610            xml.Append("<numFmts />");
611            xml.Append("<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>");
612            xml.Append("<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>");
613            xml.Append("<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>");
614            xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>");
615            xml.Append("<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>");
616            xml.Append("<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>");
617                        xml.Append("<dxfs count=\"0\" />");
618                        xml.Append("</styleSheet>");
619           
620            _stylesXml = new XmlDocument();
621            _stylesXml.LoadXml(xml.ToString());
622           
623            //Save it to the package
624            StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write));
625
626            _stylesXml.Save(stream);
627            //stream.Close();
628            _package.Package.Flush();
629
630            // create the relationship between the workbook and the new shared strings part
631            _package.Workbook.Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, StylesUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/styles");
632            _package.Package.Flush();
633          }
634        }
635        return (_stylesXml);
636      }
637      set
638      {
639        _stylesXml = value;
640      }
641    }
642    /// <summary>
643    /// Package styles collection. Used internally to access style data.
644    /// </summary>
645    public ExcelStyles Styles
646    {
647      get
648      {
649        if (_styles == null)
650        {
651          _styles = new ExcelStyles(NameSpaceManager, StylesXml, this);
652        }
653        return _styles;
654      }
655    }
656    #endregion
657
658    #region Office Document Properties
659    /// <summary>
660    /// The office document properties
661    /// </summary>
662    public OfficeProperties Properties
663    {
664      get
665      {
666        if (_properties == null)
667        {
668          //  Create a NamespaceManager to handle the default namespace,
669          //  and create a prefix for the default namespace:                   
670          _properties = new OfficeProperties(_package, NameSpaceManager);
671        }
672        return _properties;
673      }
674    }
675    #endregion
676
677    #region CalcMode
678    private string CALC_MODE_PATH = "d:calcPr/@calcMode";
679    /// <summary>
680    /// Calculation mode for the workbook.
681    /// </summary>
682    public ExcelCalcMode CalcMode
683    {
684      get
685      {
686        string calcMode = GetXmlNodeString(CALC_MODE_PATH);
687        switch (calcMode)
688        {
689          case "autoNoTable":
690            return ExcelCalcMode.AutomaticNoTable;
691          case "manual":
692            return ExcelCalcMode.Manual;
693          default:
694            return ExcelCalcMode.Automatic;
695                       
696        }
697      }
698      set
699      {
700        switch (value)
701        {
702          case ExcelCalcMode.AutomaticNoTable:
703            SetXmlNodeString(CALC_MODE_PATH, "autoNoTable") ;
704            break;
705          case ExcelCalcMode.Manual:
706            SetXmlNodeString(CALC_MODE_PATH, "manual");
707            break;
708          default:
709            SetXmlNodeString(CALC_MODE_PATH, "auto");
710            break;
711
712        }
713      }
714      #endregion
715    }
716
717        private const string FULL_CALC_ON_LOAD_PATH = "d:calcPr/@fullCalcOnLoad";
718        /// <summary>
719        /// Should Excel do a full calculation after the workbook has been loaded?
720        /// <remarks>This property is always true for both new workbooks and loaded templates(on load). If this is not the wanted behavior set this property to false.</remarks>
721        /// </summary>
722        public bool FullCalcOnLoad
723      {
724          get
725          {
726                return GetXmlNodeBool(FULL_CALC_ON_LOAD_PATH);   
727          }
728          set
729          {
730                SetXmlNodeBool(FULL_CALC_ON_LOAD_PATH, value);
731          }
732      }
733    #endregion
734    #region Workbook Private Methods
735     
736    #region Save // Workbook Save
737    /// <summary>
738    /// Saves the workbook and all its components to the package.
739    /// For internal use only!
740    /// </summary>
741    internal void Save()  // Workbook Save
742    {
743      if (Worksheets.Count == 0)
744        throw new InvalidOperationException("The workbook must contain at least one worksheet");
745
746      DeleteCalcChain();
747
748            if (_vba == null && !_package.Package.PartExists(new Uri(ExcelVbaProject.PartUri, UriKind.Relative)))
749            {
750                if (Part.ContentType != ExcelPackage.contentTypeWorkbookDefault)
751                {
752                    Part.ContentType = ExcelPackage.contentTypeWorkbookDefault;
753                }
754            }
755            else
756            {
757                if (Part.ContentType != ExcelPackage.contentTypeWorkbookMacroEnabled)
758                {
759                    Part.ContentType = ExcelPackage.contentTypeWorkbookMacroEnabled;
760                }
761            }
762     
763            UpdateDefinedNamesXml();
764
765      // save the workbook
766      if (_workbookXml != null)
767      {
768        _package.SavePart(WorkbookUri, _workbookXml);
769      }
770
771      // save the properties of the workbook
772      if (_properties != null)
773      {
774        _properties.Save();
775      }
776
777      // save the style sheet
778      Styles.UpdateXml();
779      _package.SavePart(StylesUri, _stylesXml);
780
781      // save all the open worksheets
782      var isProtected = Protection.LockWindows || Protection.LockStructure;
783      foreach (ExcelWorksheet worksheet in Worksheets)
784      {
785        if (isProtected && Protection.LockWindows)
786        {
787          worksheet.View.WindowProtection = true;
788        }
789        worksheet.Save();
790                worksheet.Part.SaveHandler = worksheet.SaveHandler;
791      }
792
793            var part = _package.Package.CreatePart(SharedStringsUri, ExcelPackage.contentTypeSharedString, _package.Compression);
794            part.SaveHandler = SaveSharedStringHandler;
795            Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/sharedStrings");
796            //UpdateSharedStringsXml();
797     
798      // Data validation
799      ValidateDataValidations();
800
801            //VBA
802            if (_vba!=null)
803            {
804#if !MONO
805                VbaProject.Save();
806#endif
807            }
808
809    }
810    private void DeleteCalcChain()
811    {
812      //Remove the calc chain if it exists.
813      Uri uriCalcChain = new Uri("/xl/calcChain.xml", UriKind.Relative);
814      if (_package.Package.PartExists(uriCalcChain))
815      {
816        Uri calcChain = new Uri("calcChain.xml", UriKind.Relative);
817        foreach (var relationship in _package.Workbook.Part.GetRelationships())
818        {
819          if (relationship.TargetUri == calcChain)
820          {
821            _package.Workbook.Part.DeleteRelationship(relationship.Id);
822            break;
823          }
824        }
825        // delete the calcChain part
826        _package.Package.DeletePart(uriCalcChain);
827      }
828    }
829
830    private void ValidateDataValidations()
831    {
832      foreach (var sheet in _package.Workbook.Worksheets)
833      {
834                if (!(sheet is ExcelChartsheet))
835                {
836                    sheet.DataValidations.ValidateAll();
837                }
838      }
839    }
840
841        private void SaveSharedStringHandler(ZipOutputStream stream, CompressionLevel compressionLevel, string fileName)
842    {
843            //Packaging.ZipPackagePart stringPart;
844            //if (_package.Package.PartExists(SharedStringsUri))
845            //{
846            //    stringPart=_package.Package.GetPart(SharedStringsUri);
847            //}
848            //else
849            //{
850            //    stringPart = _package.Package.CreatePart(SharedStringsUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", _package.Compression);
851                  //Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/sharedStrings");
852            //}
853
854      //StreamWriter sw = new StreamWriter(stringPart.GetStream(FileMode.Create, FileAccess.Write));
855            //Init Zip
856            stream.CompressionLevel = (OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel)compressionLevel;
857            stream.PutNextEntry(fileName);
858
859            var cache = new StringBuilder();           
860            var sw = new StreamWriter(stream);
861            cache.AppendFormat("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"{0}\" uniqueCount=\"{0}\">", _sharedStrings.Count);
862      foreach (string t in _sharedStrings.Keys)
863      {
864
865        SharedStringItem ssi = _sharedStrings[t];
866        if (ssi.isRichText)
867        {
868                    cache.Append("<si>");
869                    ConvertUtil.ExcelEncodeString(cache, t);
870                    cache.Append("</si>");
871        }
872        else
873        {
874          if (t.Length>0 && (t[0] == ' ' || t[t.Length-1] == ' ' || t.Contains("  ") || t.Contains("\t")))
875          {
876                        cache.Append("<si><t xml:space=\"preserve\">");
877          }
878          else
879          {
880                        cache.Append("<si><t>");
881          }
882                    ConvertUtil.ExcelEncodeString(cache, ConvertUtil.ExcelEscapeString(t));
883                    cache.Append("</t></si>");
884        }
885                if (cache.Length > 0x600000)
886                {
887                    sw.Write(cache.ToString());
888                    cache = new StringBuilder();           
889                }
890      }
891            cache.Append("</sst>");
892            sw.Write(cache.ToString());
893            sw.Flush();
894            Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/sharedStrings");
895    }
896    private void UpdateDefinedNamesXml()
897    {
898      try
899      {
900                XmlNode top = WorkbookXml.SelectSingleNode("//d:definedNames", NameSpaceManager);
901        if (!ExistsNames())
902        {
903          if (top != null) TopNode.RemoveChild(top);
904          return;
905        }
906        else
907        {
908          if (top == null)
909          {
910            CreateNode("d:definedNames");
911            top = WorkbookXml.SelectSingleNode("//d:definedNames", NameSpaceManager);
912          }
913          else
914          {
915            top.RemoveAll();
916          }
917          foreach (ExcelNamedRange name in _names)
918          {
919
920            XmlElement elem = WorkbookXml.CreateElement("definedName", ExcelPackage.schemaMain);
921            top.AppendChild(elem);
922            elem.SetAttribute("name", name.Name);
923            if (name.IsNameHidden) elem.SetAttribute("hidden", "1");
924            if (!string.IsNullOrEmpty(name.NameComment)) elem.SetAttribute("comment", name.NameComment);
925            SetNameElement(name, elem);
926          }
927        }
928        foreach (ExcelWorksheet ws in _worksheets)
929        {
930                    if (!(ws is ExcelChartsheet))
931                    {
932                        foreach (ExcelNamedRange name in ws.Names)
933                        {
934                            XmlElement elem = WorkbookXml.CreateElement("definedName", ExcelPackage.schemaMain);
935                            top.AppendChild(elem);
936                            elem.SetAttribute("name", name.Name);
937                            elem.SetAttribute("localSheetId", name.LocalSheetId.ToString());
938                            if (name.IsNameHidden) elem.SetAttribute("hidden", "1");
939                            if (!string.IsNullOrEmpty(name.NameComment)) elem.SetAttribute("comment", name.NameComment);
940                            SetNameElement(name, elem);
941                        }
942                    }
943        }
944      }
945      catch (Exception ex)
946      {
947        throw new Exception("Internal error updating named ranges ",ex);
948      }
949    }
950
951    private void SetNameElement(ExcelNamedRange name, XmlElement elem)
952    {
953      if (name.IsName)
954      {
955        if (string.IsNullOrEmpty(name.NameFormula))
956        {
957          if ((name.NameValue.GetType().IsPrimitive || name.NameValue is double || name.NameValue is decimal))
958          {
959            elem.InnerText = Convert.ToDouble(name.NameValue, CultureInfo.InvariantCulture).ToString("R15", CultureInfo.InvariantCulture);
960          }
961          else if (name.NameValue is DateTime)
962          {
963            elem.InnerText = ((DateTime)name.NameValue).ToOADate().ToString(CultureInfo.InvariantCulture);
964          }
965          else
966          {
967            elem.InnerText = "\"" + name.NameValue.ToString() + "\"";
968          }                               
969        }
970        else
971        {
972          elem.InnerText = name.NameFormula;
973        }
974      }
975      else
976      {
977                elem.InnerText = name.FullAddressAbsolute;
978      }
979    }
980    /// <summary>
981    /// Is their any names in the workbook or in the sheets.
982    /// </summary>
983    /// <returns>?</returns>
984    private bool ExistsNames()
985    {
986      if (_names.Count == 0)
987      {
988        foreach (ExcelWorksheet ws in Worksheets)
989        {
990                    if (ws is ExcelChartsheet) continue;
991                    if(ws.Names.Count>0)
992          {
993            return true;
994          }
995        }
996      }
997      else
998      {
999        return true;
1000      }
1001      return false;
1002    }       
1003    #endregion
1004
1005    #endregion
1006    internal bool ExistsTableName(string Name)
1007    {
1008      foreach (var ws in Worksheets)
1009      {
1010        if(ws.Tables._tableNames.ContainsKey(Name))
1011        {
1012          return true;
1013        }
1014      }
1015      return false;
1016    }
1017    internal bool ExistsPivotTableName(string Name)
1018    {
1019      foreach (var ws in Worksheets)
1020      {
1021        if (ws.PivotTables._pivotTableNames.ContainsKey(Name))
1022        {
1023          return true;
1024        }
1025      }
1026      return false;
1027    }
1028    internal void AddPivotTable(string cacheID, Uri defUri)
1029    {
1030      CreateNode("d:pivotCaches");
1031
1032      XmlElement item = WorkbookXml.CreateElement("pivotCache", ExcelPackage.schemaMain);
1033      item.SetAttribute("cacheId", cacheID);
1034      var rel = Part.CreateRelationship(UriHelper.ResolvePartUri(WorkbookUri, defUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/pivotCacheDefinition");
1035      item.SetAttribute("id", ExcelPackage.schemaRelationships, rel.Id);
1036
1037      var pivotCaches = WorkbookXml.SelectSingleNode("//d:pivotCaches", NameSpaceManager);
1038      pivotCaches.AppendChild(item);
1039    }
1040    internal List<string> _externalReferences = new List<string>();
1041        //internal bool _isCalculated=false;
1042    internal void GetExternalReferences()
1043    {
1044      XmlNodeList nl = WorkbookXml.SelectNodes("//d:externalReferences/d:externalReference", NameSpaceManager);
1045      if (nl != null)
1046      {
1047        foreach (XmlElement elem in nl)
1048        {
1049          string rID = elem.GetAttribute("r:id");
1050          var rel = Part.GetRelationship(rID);
1051          var part = _package.Package.GetPart(UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri));
1052          XmlDocument xmlExtRef = new XmlDocument();
1053                    LoadXmlSafe(xmlExtRef, part.GetStream());
1054
1055          XmlElement book=xmlExtRef.SelectSingleNode("//d:externalBook", NameSpaceManager) as XmlElement;
1056          if(book!=null)
1057          {
1058            string rId_ExtRef = book.GetAttribute("r:id");
1059            var rel_extRef = part.GetRelationship(rId_ExtRef);
1060            if (rel_extRef != null)
1061            {
1062              _externalReferences.Add(rel_extRef.TargetUri.OriginalString);
1063            }
1064
1065          }
1066        }
1067      }
1068    }
1069
1070        public void Dispose()
1071        {
1072            _sharedStrings.Clear();
1073            _sharedStringsList.Clear();
1074           
1075            _sharedStrings = null;
1076            _sharedStringsList = null;
1077            _vba = null;
1078            if (_worksheets != null)
1079            {
1080                _worksheets.Dispose();
1081                _worksheets = null;
1082            }
1083            _package = null;
1084            _properties = null;
1085            if (_formulaParser != null)
1086            {
1087                _formulaParser.Dispose();
1088                _formulaParser = null;
1089            }   
1090        }
1091    } // end Workbook
1092}
Note: See TracBrowser for help on using the repository browser.