| /******************************************************************************* |
| * You may amend and distribute as you like, but don't remove this header! |
| * |
| * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. |
| * See http://www.codeplex.com/EPPlus for details. |
| * |
| * Copyright (C) 2011 Jan Källman |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU Lesser General Public License for more details. |
| * |
| * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php |
| * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html |
| * |
| * All code and executables are provided "as is" with no warranty either express or implied. |
| * The author accepts no liability for any damage or loss of business that this product may cause. |
| * |
| * Code change notes: |
| * |
| * Author Change Date |
| * ****************************************************************************** |
| * Jan Källman Initial Release 2011-01-01 |
| * Jan Källman License changed GPL-->LGPL 2011-12-27 |
| * Richard Tallent Fix escaping of quotes 2012-10-31 |
| *******************************************************************************/ |
| using System; |
| using System.Xml; |
| using System.IO; |
| using System.Collections.Generic; |
| using System.Text; |
| using System.Globalization; |
| using OfficeOpenXml.Utils; |
| using OfficeOpenXml.FormulaParsing; |
| using OfficeOpenXml.FormulaParsing.LexicalAnalysis; |
| using System.Drawing; |
| |
| namespace OfficeOpenXml |
| { |
| #region Public Enum ExcelCalcMode |
| /// <summary> |
| /// How the application should calculate formulas in the workbook |
| /// </summary> |
| public enum ExcelCalcMode |
| { |
| /// <summary> |
| /// Indicates that calculations in the workbook are performed automatically when cell values change. |
| /// The application recalculates those cells that are dependent on other cells that contain changed values. |
| /// This mode of calculation helps to avoid unnecessary calculations. |
| /// </summary> |
| Automatic, |
| /// <summary> |
| /// Indicates tables be excluded during automatic calculation |
| /// </summary> |
| AutomaticNoTable, |
| /// <summary> |
| /// Indicates that calculations in the workbook be triggered manually by the user. |
| /// </summary> |
| Manual |
| } |
| #endregion |
| |
| /// <summary> |
| /// Represents the Excel workbook and provides access to all the |
| /// document properties and worksheets within the workbook. |
| /// </summary> |
| public sealed class ExcelWorkbook : XmlHelper |
| { |
| internal class SharedStringItem |
| { |
| internal int pos; |
| internal string Text; |
| internal bool isRichText = false; |
| } |
| #region Private Properties |
| internal ExcelPackage _package; |
| private ExcelWorksheets _worksheets; |
| private OfficeProperties _properties; |
| |
| private ExcelStyles _styles; |
| #endregion |
| |
| #region ExcelWorkbook Constructor |
| /// <summary> |
| /// Creates a new instance of the ExcelWorkbook class. |
| /// </summary> |
| /// <param name="package">The parent package</param> |
| /// <param name="namespaceManager">NamespaceManager</param> |
| internal ExcelWorkbook(ExcelPackage package, XmlNamespaceManager namespaceManager) : |
| base(namespaceManager) |
| { |
| _package = package; |
| WorkbookUri = new Uri("/xl/workbook.xml", UriKind.Relative); |
| SharedStringsUri = new Uri("/xl/sharedStrings.xml", UriKind.Relative); |
| StylesUri = new Uri("/xl/styles.xml", UriKind.Relative); |
| |
| _names = new ExcelNamedRangeCollection(this); |
| _namespaceManager = namespaceManager; |
| TopNode = WorkbookXml.DocumentElement; |
| SchemaNodeOrder = new string[] { "fileVersion", "fileSharing", "workbookPr", "workbookProtection", "bookViews", "sheets", "functionGroups", "functionPrototypes", "externalReferences", "definedNames", "calcPr", "oleSize", "customWorkbookViews", "pivotCaches", "smartTagPr", "smartTagTypes", "webPublishing", "fileRecoveryPr", }; |
| FullCalcOnLoad = true; //Full calculation on load by default, for both new workbooks and templates. |
| GetSharedStrings(); |
| } |
| #endregion |
| |
| internal Dictionary<string, SharedStringItem> _sharedStrings = new Dictionary<string, SharedStringItem>(); //Used when reading cells. |
| internal List<SharedStringItem> _sharedStringsList = new List<SharedStringItem>(); //Used when reading cells. |
| internal ExcelNamedRangeCollection _names; |
| internal int _nextDrawingID = 0; |
| internal int _nextTableID = int.MinValue; |
| internal int _nextPivotTableID = int.MinValue; |
| internal XmlNamespaceManager _namespaceManager; |
| internal FormulaParser _formulaParser = null; |
| internal FormulaParserManager _parserManager; |
| internal CellStore<List<Token>> _formulaTokens; |
| /// <summary> |
| /// Read shared strings to list |
| /// </summary> |
| private void GetSharedStrings() |
| { |
| if (_package.Package.PartExists(SharedStringsUri)) |
| { |
| var xml = _package.GetXmlFromUri(SharedStringsUri); |
| XmlNodeList nl = xml.SelectNodes("//d:sst/d:si", NameSpaceManager); |
| _sharedStringsList = new List<SharedStringItem>(); |
| if (nl != null) |
| { |
| foreach (XmlNode node in nl) |
| { |
| XmlNode n = node.SelectSingleNode("d:t", NameSpaceManager); |
| if (n != null) |
| { |
| _sharedStringsList.Add(new SharedStringItem() { Text = ConvertUtil.ExcelDecodeString(n.InnerText) }); |
| } |
| else |
| { |
| _sharedStringsList.Add(new SharedStringItem() { Text = node.InnerXml, isRichText = true }); |
| } |
| } |
| } |
| //Delete the shared string part, it will be recreated when the package is saved. |
| foreach (var rel in Part.GetRelationships()) |
| { |
| if (rel.TargetUri.OriginalString.EndsWith("sharedstrings.xml", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| Part.DeleteRelationship(rel.Id); |
| break; |
| } |
| } |
| _package.Package.DeletePart(SharedStringsUri); //Remove the part, it is recreated when saved. |
| } |
| } |
| internal void GetDefinedNames() |
| { |
| XmlNodeList nl = WorkbookXml.SelectNodes("//d:definedNames/d:definedName", NameSpaceManager); |
| if (nl != null) |
| { |
| foreach (XmlElement elem in nl) |
| { |
| string fullAddress = elem.InnerText; |
| |
| int localSheetID; |
| ExcelWorksheet nameWorksheet; |
| if(!int.TryParse(elem.GetAttribute("localSheetId"), out localSheetID)) |
| { |
| localSheetID = -1; |
| nameWorksheet=null; |
| } |
| else |
| { |
| nameWorksheet=Worksheets[localSheetID + 1]; |
| } |
| var addressType = ExcelAddressBase.IsValid(fullAddress); |
| ExcelRangeBase range; |
| ExcelNamedRange namedRange; |
| |
| if (fullAddress.IndexOf("[") == 0) |
| { |
| int start = fullAddress.IndexOf("["); |
| int end = fullAddress.IndexOf("]", start); |
| if (start >= 0 && end >= 0) |
| { |
| |
| string externalIndex = fullAddress.Substring(start + 1, end - start - 1); |
| int index; |
| if (int.TryParse(externalIndex, out index)) |
| { |
| if (index > 0 && index <= _externalReferences.Count) |
| { |
| fullAddress = fullAddress.Substring(0, start) + "[" + _externalReferences[index - 1] + "]" + fullAddress.Substring(end + 1); |
| } |
| } |
| } |
| } |
| |
| 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 |
| { |
| double value; |
| range = new ExcelRangeBase(this, nameWorksheet, elem.GetAttribute("name"), true); |
| if (nameWorksheet == null) |
| { |
| namedRange = _names.Add(elem.GetAttribute("name"), range); |
| } |
| else |
| { |
| namedRange = nameWorksheet.Names.Add(elem.GetAttribute("name"), range); |
| } |
| |
| if (fullAddress.StartsWith("\"")) //String value |
| { |
| namedRange.NameValue = fullAddress.Substring(1,fullAddress.Length-2); |
| } |
| else if (double.TryParse(fullAddress, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) |
| { |
| namedRange.NameValue = value; |
| } |
| else |
| { |
| //if (addressType == ExcelAddressBase.AddressType.ExternalAddress || addressType == ExcelAddressBase.AddressType.ExternalName) |
| //{ |
| // var r = new ExcelAddress(fullAddress); |
| // namedRange.NameFormula = '\'[' + r._wb |
| //} |
| //else |
| //{ |
| namedRange.NameFormula = fullAddress; |
| //} |
| } |
| } |
| else |
| { |
| ExcelAddress addr = new ExcelAddress(fullAddress, _package, null); |
| if (localSheetID > -1) |
| { |
| if (string.IsNullOrEmpty(addr._ws)) |
| { |
| namedRange = Worksheets[localSheetID + 1].Names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, Worksheets[localSheetID + 1], fullAddress, false)); |
| } |
| else |
| { |
| namedRange = Worksheets[localSheetID + 1].Names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, Worksheets[addr._ws], fullAddress, false)); |
| } |
| } |
| else |
| { |
| var ws = Worksheets[addr._ws]; |
| namedRange = _names.Add(elem.GetAttribute("name"), new ExcelRangeBase(this, ws, fullAddress, false)); |
| } |
| } |
| if (elem.GetAttribute("hidden") == "1" && namedRange != null) namedRange.IsNameHidden = true; |
| if(!string.IsNullOrEmpty(elem.GetAttribute("comment"))) namedRange.NameComment=elem.GetAttribute("comment"); |
| } |
| } |
| } |
| #region Worksheets |
| /// <summary> |
| /// Provides access to all the worksheets in the workbook. |
| /// </summary> |
| public ExcelWorksheets Worksheets |
| { |
| get |
| { |
| if (_worksheets == null) |
| { |
| var sheetsNode = _workbookXml.DocumentElement.SelectSingleNode("d:sheets", _namespaceManager); |
| if (sheetsNode == null) |
| { |
| sheetsNode = CreateNode("d:sheets"); |
| } |
| |
| _worksheets = new ExcelWorksheets(_package, _namespaceManager, sheetsNode); |
| } |
| return (_worksheets); |
| } |
| } |
| #endregion |
| |
| /// <summary> |
| /// Provides access to named ranges |
| /// </summary> |
| public ExcelNamedRangeCollection Names |
| { |
| get |
| { |
| return _names; |
| } |
| } |
| #region Workbook Properties |
| internal FormulaParser FormulaParser |
| { |
| get |
| { |
| if (_formulaParser == null) |
| { |
| _formulaParser = new FormulaParser(new EpplusExcelDataProvider(_package)); |
| } |
| return _formulaParser; |
| } |
| } |
| |
| public FormulaParserManager FormulaParserManager |
| { |
| get |
| { |
| if (_parserManager == null) |
| { |
| _parserManager = new FormulaParserManager(FormulaParser); |
| } |
| return _parserManager; |
| } |
| } |
| |
| ExcelProtection _protection = null; |
| /// <summary> |
| /// Access properties to protect or unprotect a workbook |
| /// </summary> |
| public ExcelProtection Protection |
| { |
| get |
| { |
| if (_protection == null) |
| { |
| _protection = new ExcelProtection(NameSpaceManager, TopNode, this); |
| _protection.SchemaNodeOrder = SchemaNodeOrder; |
| } |
| return _protection; |
| } |
| } |
| ExcelWorkbookView _view = null; |
| /// <summary> |
| /// Access to workbook view properties |
| /// </summary> |
| public ExcelWorkbookView View |
| { |
| get |
| { |
| if (_view == null) |
| { |
| _view = new ExcelWorkbookView(NameSpaceManager, TopNode, this); |
| } |
| return _view; |
| } |
| } |
| |
| /// <summary> |
| /// URI to the workbook inside the package |
| /// </summary> |
| internal Uri WorkbookUri { get; private set; } |
| /// <summary> |
| /// URI to the styles inside the package |
| /// </summary> |
| internal Uri StylesUri { get; private set; } |
| /// <summary> |
| /// URI to the shared strings inside the package |
| /// </summary> |
| internal Uri SharedStringsUri { get; private set; } |
| /// <summary> |
| /// Returns a reference to the workbook's part within the package |
| /// </summary> |
| internal Packaging.ZipPackagePart Part { get { return (_package.Package.GetPart(WorkbookUri)); } } |
| |
| #region WorkbookXml |
| private XmlDocument _workbookXml; |
| /// <summary> |
| /// Provides access to the XML data representing the workbook in the package. |
| /// </summary> |
| public XmlDocument WorkbookXml |
| { |
| get |
| { |
| if (_workbookXml == null) |
| { |
| CreateWorkbookXml(_namespaceManager); |
| } |
| return (_workbookXml); |
| } |
| } |
| const string codeModuleNamePath = "d:workbookPr/@codeName"; |
| internal string CodeModuleName |
| { |
| get |
| { |
| return GetXmlNodeString(codeModuleNamePath); |
| } |
| set |
| { |
| SetXmlNodeString(codeModuleNamePath,value); |
| } |
| } |
| internal void CodeNameChange(string value) |
| { |
| CodeModuleName = value; |
| } |
| |
| const string date1904Path = "d:workbookPr/@date1904"; |
| internal const double date1904Offset = 365.5 * 4; // offset to fix 1900 and 1904 differences, 4 OLE years |
| /// <summary> |
| /// 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. |
| /// The default for the serial number 1 can be changed to represent January 2, 1904. |
| /// This option was included in Microsoft Excel for Windows to make it compatible with Excel for the Macintosh, which defaults to January 2, 1904. |
| /// </summary> |
| public bool Date1904 |
| { |
| get |
| { |
| return GetXmlNodeBool(date1904Path, false); |
| |
| } |
| set |
| { |
| if (Date1904 != value) |
| { |
| // Like Excel when the option it's changed update it all cells with Date format |
| foreach (var item in Worksheets) |
| { |
| item.UpdateCellsWithDate1904Setting(); |
| } |
| } |
| |
| SetXmlNodeBool(date1904Path, value, false); |
| } |
| } |
| |
| |
| /// <summary> |
| /// Create or read the XML for the workbook. |
| /// </summary> |
| private void CreateWorkbookXml(XmlNamespaceManager namespaceManager) |
| { |
| if (_package.Package.PartExists(WorkbookUri)) |
| _workbookXml = _package.GetXmlFromUri(WorkbookUri); |
| else |
| { |
| // create a new workbook part and add to the package |
| Packaging.ZipPackagePart partWorkbook = _package.Package.CreatePart(WorkbookUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", _package.Compression); |
| |
| // create the workbook |
| _workbookXml = new XmlDocument(namespaceManager.NameTable); |
| |
| _workbookXml.PreserveWhitespace = ExcelPackage.preserveWhitespace; |
| // create the workbook element |
| XmlElement wbElem = _workbookXml.CreateElement("workbook", ExcelPackage.schemaMain); |
| |
| // Add the relationships namespace |
| wbElem.SetAttribute("xmlns:r", ExcelPackage.schemaRelationships); |
| |
| _workbookXml.AppendChild(wbElem); |
| |
| // create the bookViews and workbooks element |
| XmlElement bookViews = _workbookXml.CreateElement("bookViews", ExcelPackage.schemaMain); |
| wbElem.AppendChild(bookViews); |
| XmlElement workbookView = _workbookXml.CreateElement("workbookView", ExcelPackage.schemaMain); |
| bookViews.AppendChild(workbookView); |
| |
| // save it to the package |
| StreamWriter stream = new StreamWriter(partWorkbook.GetStream(FileMode.Create, FileAccess.Write)); |
| _workbookXml.Save(stream); |
| //stream.Close(); |
| } |
| } |
| #endregion |
| #region StylesXml |
| private XmlDocument _stylesXml; |
| /// <summary> |
| /// Provides access to the XML data representing the styles in the package. |
| /// </summary> |
| public XmlDocument StylesXml |
| { |
| get |
| { |
| if (_stylesXml == null) |
| { |
| if (_package.Package.PartExists(StylesUri)) |
| _stylesXml = _package.GetXmlFromUri(StylesUri); |
| else |
| { |
| // create a new styles part and add to the package |
| Packaging.ZipPackagePart part = _package.Package.CreatePart(StylesUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", _package.Compression); |
| // create the style sheet |
| |
| StringBuilder xml = new StringBuilder("<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"); |
| xml.Append("<numFmts />"); |
| xml.Append("<fonts count=\"1\"><font><sz val=\"11\" /><name val=\"Calibri\" /></font></fonts>"); |
| xml.Append("<fills><fill><patternFill patternType=\"none\" /></fill><fill><patternFill patternType=\"gray125\" /></fill></fills>"); |
| xml.Append("<borders><border><left /><right /><top /><bottom /><diagonal /></border></borders>"); |
| xml.Append("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" /></cellStyleXfs>"); |
| xml.Append("<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" xfId=\"0\" /></cellXfs>"); |
| xml.Append("<cellStyles><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\" /></cellStyles>"); |
| xml.Append("<dxfs count=\"0\" />"); |
| xml.Append("</styleSheet>"); |
| |
| _stylesXml = new XmlDocument(); |
| _stylesXml.LoadXml(xml.ToString()); |
| |
| //Save it to the package |
| StreamWriter stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write)); |
| |
| _stylesXml.Save(stream); |
| //stream.Close(); |
| |
| // create the relationship between the workbook and the new shared strings part |
| _package.Workbook.Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, StylesUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/styles"); |
| } |
| } |
| return (_stylesXml); |
| } |
| set |
| { |
| _stylesXml = value; |
| } |
| } |
| /// <summary> |
| /// Package styles collection. Used internally to access style data. |
| /// </summary> |
| public ExcelStyles Styles |
| { |
| get |
| { |
| if (_styles == null) |
| { |
| _styles = new ExcelStyles(NameSpaceManager, StylesXml, this); |
| } |
| return _styles; |
| } |
| } |
| #endregion |
| |
| #region Office Document Properties |
| /// <summary> |
| /// The office document properties |
| /// </summary> |
| public OfficeProperties Properties |
| { |
| get |
| { |
| if (_properties == null) |
| { |
| // Create a NamespaceManager to handle the default namespace, |
| // and create a prefix for the default namespace: |
| _properties = new OfficeProperties(_package, NameSpaceManager); |
| } |
| return _properties; |
| } |
| } |
| #endregion |
| |
| #region CalcMode |
| private string CALC_MODE_PATH = "d:calcPr/@calcMode"; |
| /// <summary> |
| /// Calculation mode for the workbook. |
| /// </summary> |
| public ExcelCalcMode CalcMode |
| { |
| get |
| { |
| string calcMode = GetXmlNodeString(CALC_MODE_PATH); |
| switch (calcMode) |
| { |
| case "autoNoTable": |
| return ExcelCalcMode.AutomaticNoTable; |
| case "manual": |
| return ExcelCalcMode.Manual; |
| default: |
| return ExcelCalcMode.Automatic; |
| |
| } |
| } |
| set |
| { |
| switch (value) |
| { |
| case ExcelCalcMode.AutomaticNoTable: |
| SetXmlNodeString(CALC_MODE_PATH, "autoNoTable") ; |
| break; |
| case ExcelCalcMode.Manual: |
| SetXmlNodeString(CALC_MODE_PATH, "manual"); |
| break; |
| default: |
| SetXmlNodeString(CALC_MODE_PATH, "auto"); |
| break; |
| |
| } |
| } |
| #endregion |
| } |
| |
| private const string FULL_CALC_ON_LOAD_PATH = "d:calcPr/@fullCalcOnLoad"; |
| /// <summary> |
| /// Should Excel do a full calculation after the workbook has been loaded? |
| /// <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> |
| /// </summary> |
| public bool FullCalcOnLoad |
| { |
| get |
| { |
| return GetXmlNodeBool(FULL_CALC_ON_LOAD_PATH); |
| } |
| set |
| { |
| SetXmlNodeBool(FULL_CALC_ON_LOAD_PATH, value); |
| } |
| } |
| #endregion |
| #region Workbook Private Methods |
| |
| #region Save // Workbook Save |
| /// <summary> |
| /// Saves the workbook and all its components to the package. |
| /// For internal use only! |
| /// </summary> |
| internal void Save() // Workbook Save |
| { |
| if (Worksheets.Count == 0) |
| throw new InvalidOperationException("The workbook must contain at least one worksheet"); |
| |
| DeleteCalcChain(); |
| |
| const string vbaPartUri = "/xl/vbaProject.bin"; |
| if (!_package.Package.PartExists(new Uri(vbaPartUri, UriKind.Relative))) |
| { |
| if (Part.ContentType != ExcelPackage.contentTypeWorkbookDefault) |
| { |
| Part.ContentType = ExcelPackage.contentTypeWorkbookDefault; |
| } |
| } |
| else |
| { |
| if (Part.ContentType != ExcelPackage.contentTypeWorkbookMacroEnabled) |
| { |
| Part.ContentType = ExcelPackage.contentTypeWorkbookMacroEnabled; |
| } |
| } |
| |
| UpdateDefinedNamesXml(); |
| |
| // save the workbook |
| if (_workbookXml != null) |
| { |
| _package.SavePart(WorkbookUri, _workbookXml); |
| } |
| |
| // save the properties of the workbook |
| if (_properties != null) |
| { |
| _properties.Save(); |
| } |
| |
| // save the style sheet |
| Styles.UpdateXml(); |
| _package.SavePart(StylesUri, _stylesXml); |
| |
| // save all the open worksheets |
| var isProtected = Protection.LockWindows || Protection.LockStructure; |
| foreach (ExcelWorksheet worksheet in Worksheets) |
| { |
| if (isProtected && Protection.LockWindows) |
| { |
| worksheet.View.WindowProtection = true; |
| } |
| worksheet.Save(); |
| worksheet.Part.SaveHandler = worksheet.SaveHandler; |
| } |
| |
| var part = _package.Package.CreatePart(SharedStringsUri, ExcelPackage.contentTypeSharedString, _package.Compression); |
| part.SaveHandler = SaveSharedStringHandler; |
| Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/sharedStrings"); |
| //UpdateSharedStringsXml(); |
| |
| // Data validation |
| ValidateDataValidations(); |
| } |
| private void DeleteCalcChain() |
| { |
| //Remove the calc chain if it exists. |
| Uri uriCalcChain = new Uri("/xl/calcChain.xml", UriKind.Relative); |
| if (_package.Package.PartExists(uriCalcChain)) |
| { |
| Uri calcChain = new Uri("calcChain.xml", UriKind.Relative); |
| foreach (var relationship in _package.Workbook.Part.GetRelationships()) |
| { |
| if (relationship.TargetUri == calcChain) |
| { |
| _package.Workbook.Part.DeleteRelationship(relationship.Id); |
| break; |
| } |
| } |
| // delete the calcChain part |
| _package.Package.DeletePart(uriCalcChain); |
| } |
| } |
| |
| private void ValidateDataValidations() |
| { |
| foreach (var sheet in _package.Workbook.Worksheets) |
| { |
| if (!(sheet is ExcelChartsheet)) |
| { |
| sheet.DataValidations.ValidateAll(); |
| } |
| } |
| } |
| |
| private void SaveSharedStringHandler(StreamWriter sw) |
| { |
| var cache = new StringBuilder(); |
| 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); |
| foreach (string t in _sharedStrings.Keys) |
| { |
| |
| SharedStringItem ssi = _sharedStrings[t]; |
| if (ssi.isRichText) |
| { |
| cache.Append("<si>"); |
| ConvertUtil.ExcelEncodeString(cache, t); |
| cache.Append("</si>"); |
| } |
| else |
| { |
| if (t.Length > 0 && (t[0] == ' ' || t[t.Length - 1] == ' ' || t.Contains(" ") || t.Contains("\t") || t.Contains("\n") || t.Contains("\n"))) //Fixes issue 14849 |
| { |
| cache.Append("<si><t xml:space=\"preserve\">"); |
| } |
| else |
| { |
| cache.Append("<si><t>"); |
| } |
| ConvertUtil.ExcelEncodeString(cache, ConvertUtil.ExcelEscapeString(t)); |
| cache.Append("</t></si>"); |
| } |
| if (cache.Length > 0x600000) |
| { |
| sw.Write(cache.ToString()); |
| cache = new StringBuilder(); |
| } |
| } |
| cache.Append("</sst>"); |
| sw.Write(cache.ToString()); |
| sw.Flush(); |
| Part.CreateRelationship(UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/sharedStrings"); |
| } |
| private void UpdateDefinedNamesXml() |
| { |
| try |
| { |
| XmlNode top = WorkbookXml.SelectSingleNode("//d:definedNames", NameSpaceManager); |
| if (!ExistsNames()) |
| { |
| if (top != null) TopNode.RemoveChild(top); |
| return; |
| } |
| else |
| { |
| if (top == null) |
| { |
| CreateNode("d:definedNames"); |
| top = WorkbookXml.SelectSingleNode("//d:definedNames", NameSpaceManager); |
| } |
| else |
| { |
| top.RemoveAll(); |
| } |
| foreach (ExcelNamedRange name in _names) |
| { |
| |
| XmlElement elem = WorkbookXml.CreateElement("definedName", ExcelPackage.schemaMain); |
| top.AppendChild(elem); |
| elem.SetAttribute("name", name.Name); |
| if (name.IsNameHidden) elem.SetAttribute("hidden", "1"); |
| if (!string.IsNullOrEmpty(name.NameComment)) elem.SetAttribute("comment", name.NameComment); |
| SetNameElement(name, elem); |
| } |
| } |
| foreach (ExcelWorksheet ws in _worksheets) |
| { |
| if (!(ws is ExcelChartsheet)) |
| { |
| foreach (ExcelNamedRange name in ws.Names) |
| { |
| XmlElement elem = WorkbookXml.CreateElement("definedName", ExcelPackage.schemaMain); |
| top.AppendChild(elem); |
| elem.SetAttribute("name", name.Name); |
| elem.SetAttribute("localSheetId", name.LocalSheetId.ToString()); |
| if (name.IsNameHidden) elem.SetAttribute("hidden", "1"); |
| if (!string.IsNullOrEmpty(name.NameComment)) elem.SetAttribute("comment", name.NameComment); |
| SetNameElement(name, elem); |
| } |
| } |
| } |
| } |
| catch (Exception ex) |
| { |
| throw new Exception("Internal error updating named ranges ",ex); |
| } |
| } |
| |
| private void SetNameElement(ExcelNamedRange name, XmlElement elem) |
| { |
| if (name.IsName) |
| { |
| if (string.IsNullOrEmpty(name.NameFormula)) |
| { |
| if ((name.NameValue.GetType().IsPrimitive || name.NameValue is double || name.NameValue is decimal)) |
| { |
| elem.InnerText = Convert.ToDouble(name.NameValue, CultureInfo.InvariantCulture).ToString("R15", CultureInfo.InvariantCulture); |
| } |
| else if (name.NameValue is DateTime) |
| { |
| elem.InnerText = ((DateTime)name.NameValue).ToOADate().ToString(CultureInfo.InvariantCulture); |
| } |
| else |
| { |
| elem.InnerText = "\"" + name.NameValue.ToString() + "\""; |
| } |
| } |
| else |
| { |
| elem.InnerText = name.NameFormula; |
| } |
| } |
| else |
| { |
| elem.InnerText = name.FullAddressAbsolute; |
| } |
| } |
| /// <summary> |
| /// Is their any names in the workbook or in the sheets. |
| /// </summary> |
| /// <returns>?</returns> |
| private bool ExistsNames() |
| { |
| if (_names.Count == 0) |
| { |
| foreach (ExcelWorksheet ws in Worksheets) |
| { |
| if (ws is ExcelChartsheet) continue; |
| if(ws.Names.Count>0) |
| { |
| return true; |
| } |
| } |
| } |
| else |
| { |
| return true; |
| } |
| return false; |
| } |
| #endregion |
| |
| #endregion |
| internal bool ExistsTableName(string Name) |
| { |
| foreach (var ws in Worksheets) |
| { |
| if(ws.Tables._tableNames.ContainsKey(Name)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| internal bool ExistsPivotTableName(string Name) |
| { |
| foreach (var ws in Worksheets) |
| { |
| if (ws.PivotTables._pivotTableNames.ContainsKey(Name)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| internal void AddPivotTable(string cacheID, Uri defUri) |
| { |
| CreateNode("d:pivotCaches"); |
| |
| XmlElement item = WorkbookXml.CreateElement("pivotCache", ExcelPackage.schemaMain); |
| item.SetAttribute("cacheId", cacheID); |
| var rel = Part.CreateRelationship(UriHelper.ResolvePartUri(WorkbookUri, defUri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/pivotCacheDefinition"); |
| item.SetAttribute("id", ExcelPackage.schemaRelationships, rel.Id); |
| |
| var pivotCaches = WorkbookXml.SelectSingleNode("//d:pivotCaches", NameSpaceManager); |
| pivotCaches.AppendChild(item); |
| } |
| internal List<string> _externalReferences = new List<string>(); |
| //internal bool _isCalculated=false; |
| internal void GetExternalReferences() |
| { |
| XmlNodeList nl = WorkbookXml.SelectNodes("//d:externalReferences/d:externalReference", NameSpaceManager); |
| if (nl != null) |
| { |
| foreach (XmlElement elem in nl) |
| { |
| string rID = elem.GetAttribute("r:id"); |
| var rel = Part.GetRelationship(rID); |
| var part = _package.Package.GetPart(UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri)); |
| XmlDocument xmlExtRef = new XmlDocument(); |
| LoadXmlSafe(xmlExtRef, part.GetStream()); |
| |
| XmlElement book=xmlExtRef.SelectSingleNode("//d:externalBook", NameSpaceManager) as XmlElement; |
| if(book!=null) |
| { |
| string rId_ExtRef = book.GetAttribute("r:id"); |
| var rel_extRef = part.GetRelationship(rId_ExtRef); |
| if (rel_extRef != null) |
| { |
| _externalReferences.Add(rel_extRef.TargetUri.OriginalString); |
| } |
| |
| } |
| } |
| } |
| } |
| |
| internal void ReadAllTables() |
| { |
| if (_nextTableID > 0) return; |
| _nextTableID = 1; |
| _nextPivotTableID = 1; |
| foreach (var ws in Worksheets) |
| { |
| if (!(ws is ExcelChartsheet)) //Fixes 15273. Chartsheets should be ignored. |
| { |
| foreach (var tbl in ws.Tables) |
| { |
| if (tbl.Id >= _nextTableID) |
| { |
| _nextTableID = tbl.Id + 1; |
| } |
| } |
| foreach (var pt in ws.PivotTables) |
| { |
| if (pt.CacheID >= _nextPivotTableID) |
| { |
| _nextPivotTableID = pt.CacheID + 1; |
| } |
| } |
| } |
| } |
| } |
| } // end Workbook |
| } |