| /******************************************************************************* |
| * 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.Collections.Generic; |
| using System.Collections.Immutable; |
| using System.Globalization; |
| using System.IO; |
| using System.Text; |
| using System.Xml; |
| using OfficeOpenXml.FormulaParsing; |
| using OfficeOpenXml.FormulaParsing.LexicalAnalysis; |
| using OfficeOpenXml.Packaging; |
| using OfficeOpenXml.Utils; |
| |
| namespace OfficeOpenXml; |
| |
| /// <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, |
| } |
| |
| /// <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; |
| } |
| |
| private readonly ExcelPackage _package; |
| private ExcelWorksheets _worksheets; |
| private OfficeProperties _properties; |
| |
| private ExcelStyles _styles; |
| |
| internal static ImmutableArray<string> WorkbookSchemaNodeOrder = [ |
| "fileVersion", |
| "fileSharing", |
| "workbookPr", |
| "workbookProtection", |
| "bookViews", |
| "sheets", |
| "functionGroups", |
| "functionPrototypes", |
| "externalReferences", |
| "definedNames", |
| "calcPr", |
| "oleSize", |
| "customWorkbookViews", |
| "pivotCaches", |
| "smartTagPr", |
| "smartTagTypes", |
| "webPublishing", |
| "fileRecoveryPr", |
| ]; |
| |
| protected override ImmutableArray<string> SchemaNodeOrder => WorkbookSchemaNodeOrder; |
| |
| internal ExcelWorkbook(ExcelPackage package, XmlNamespaceManager namespaceManager) |
| : base(namespaceManager) { |
| _package = package; |
| _names = new(this); |
| _namespaceManager = namespaceManager; |
| |
| WorkbookXml = package.GetOrCreateXmlDocument( |
| WorkbookUri, |
| "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", |
| ExcelPackage._schemaRelationships + "/officeDocument", |
| () => CreateEmptyWorkbookXml(namespaceManager)); |
| _stylesXml = package.GetOrCreateXmlDocument( |
| StylesUri, |
| "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", |
| ExcelPackage._schemaRelationships + "/styles", |
| CreateEmptyStylesXml); |
| |
| TopNode = WorkbookXml.DocumentElement; |
| FullCalcOnLoad = true; //Full calculation on load by default, for both new workbooks and templates. |
| |
| GetSharedStrings(); |
| GetExternalReferences(); |
| GetDefinedNames(); |
| } |
| |
| private static XmlDocument CreateEmptyWorkbookXml(XmlNamespaceManager namespaceManager) { |
| var result = new XmlDocument(namespaceManager.NameTable); |
| var wbElem = result.CreateElement("workbook", ExcelPackage._schemaMain); |
| |
| // Add the relationships namespace |
| wbElem.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships); |
| result.AppendChild(wbElem); |
| |
| // Create the bookViews and workbooks element |
| var bookViews = result.CreateElement("bookViews", ExcelPackage._schemaMain); |
| wbElem.AppendChild(bookViews); |
| var workbookView = result.CreateElement("workbookView", ExcelPackage._schemaMain); |
| bookViews.AppendChild(workbookView); |
| |
| return result; |
| } |
| |
| private static XmlDocument CreateEmptyStylesXml() { |
| 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>"); |
| |
| var result = new XmlDocument(); |
| result.LoadXml(xml.ToString()); |
| return result; |
| } |
| |
| internal readonly Dictionary<string, SharedStringItem> _sharedStrings = new(); //Used when reading cells. |
| internal List<SharedStringItem> _sharedStringsList = new(); //Used when reading cells. |
| internal ExcelNamedRangeCollection _names; |
| internal int _nextTableID = int.MinValue; |
| internal int _nextPivotTableID = int.MinValue; |
| private readonly XmlNamespaceManager _namespaceManager; |
| private FormulaParser _formulaParser; |
| private 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.GetXmlDocument(SharedStringsUri); |
| XmlNodeList nl = xml.SelectNodes("//d:sst/d:si", NameSpaceManager); |
| _sharedStringsList = new(); |
| if (nl != null) { |
| foreach (XmlNode node in nl) { |
| XmlNode n = node.SelectSingleNode("d:t", NameSpaceManager); |
| if (n != null) { |
| _sharedStringsList.Add( |
| new() { |
| Text = ConvertUtil.ExcelDecodeString(n.InnerText), |
| }); |
| } else { |
| _sharedStringsList.Add( |
| new() { |
| 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; |
| |
| ExcelWorksheet nameWorksheet; |
| if (!int.TryParse(elem.GetAttribute("localSheetId"), out var 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); |
| if (int.TryParse(externalIndex, out var 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 |
| { |
| range = new(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 var 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, this, null); |
| if (localSheetId > -1) { |
| if (string.IsNullOrEmpty(addr._ws)) { |
| namedRange = Worksheets[localSheetId + 1].Names.Add( |
| elem.GetAttribute("name"), |
| new(this, Worksheets[localSheetId + 1], fullAddress, false)); |
| } else { |
| namedRange = Worksheets[localSheetId + 1].Names.Add( |
| elem.GetAttribute("name"), |
| new(this, Worksheets[addr._ws], fullAddress, false)); |
| } |
| } else { |
| var ws = Worksheets[addr._ws]; |
| namedRange = _names.Add(elem.GetAttribute("name"), new(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"); |
| } |
| } |
| } |
| } |
| |
| /// <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(_package, this, _namespaceManager, sheetsNode); |
| } |
| return (_worksheets); |
| } |
| } |
| |
| /// <summary> |
| /// Provides access to named ranges |
| /// </summary> |
| public ExcelNamedRangeCollection Names => _names; |
| |
| internal FormulaParser FormulaParser { |
| get { |
| if (_formulaParser == null) { |
| _formulaParser = new(new EpplusExcelDataProvider(this)); |
| } |
| return _formulaParser; |
| } |
| } |
| |
| public FormulaParserManager FormulaParserManager { |
| get { |
| if (_parserManager == null) { |
| _parserManager = new(FormulaParser); |
| } |
| return _parserManager; |
| } |
| } |
| |
| private ExcelProtection _protection; |
| |
| /// <summary> |
| /// Access properties to protect or unprotect a workbook |
| /// </summary> |
| public ExcelProtection Protection => _protection ??= new(NameSpaceManager, TopNode); |
| |
| private ExcelWorkbookView _view; |
| |
| /// <summary> |
| /// Access to workbook view properties |
| /// </summary> |
| public ExcelWorkbookView View { |
| get { |
| if (_view == null) { |
| _view = new(NameSpaceManager, TopNode, this); |
| } |
| return _view; |
| } |
| } |
| |
| /// <summary> |
| /// URI to the workbook inside the package |
| /// </summary> |
| internal static Uri WorkbookUri { get; } = new("/xl/workbook.xml", UriKind.Relative); |
| |
| /// <summary> |
| /// URI to the styles inside the package |
| /// </summary> |
| private static Uri StylesUri { get; } = new("/xl/styles.xml", UriKind.Relative); |
| |
| /// <summary> |
| /// URI to the shared strings inside the package |
| /// </summary> |
| private static Uri SharedStringsUri { get; } = new("/xl/sharedStrings.xml", UriKind.Relative); |
| |
| /// <summary> |
| /// Returns a reference to the workbook's part within the package |
| /// </summary> |
| internal ZipPackagePart Part => (_package.Package.GetPart(WorkbookUri)); |
| |
| /// <summary> |
| /// Provides access to the XML data representing the workbook in the package. |
| /// </summary> |
| internal XmlDocument WorkbookXml { get; } |
| |
| private const string _codeModuleNamePath = "d:workbookPr/@codeName"; |
| |
| internal string CodeModuleName { |
| get => GetXmlNodeString(_codeModuleNamePath); |
| set => SetXmlNodeString(_codeModuleNamePath, value); |
| } |
| |
| internal void CodeNameChange(string value) { |
| CodeModuleName = value; |
| } |
| |
| private 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 => 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); |
| } |
| } |
| |
| private readonly XmlDocument _stylesXml; |
| |
| /// <summary> |
| /// Package styles collection. Used internally to access style data. |
| /// </summary> |
| public ExcelStyles Styles { |
| get { |
| if (_styles == null) { |
| _styles = new(NameSpaceManager, _stylesXml, this); |
| } |
| return _styles; |
| } |
| } |
| |
| /// <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(_package, NameSpaceManager); |
| } |
| return _properties; |
| } |
| } |
| |
| private readonly string _calcModePath = "d:calcPr/@calcMode"; |
| |
| /// <summary> |
| /// Calculation mode for the workbook. |
| /// </summary> |
| public ExcelCalcMode CalcMode { |
| get { |
| string calcMode = GetXmlNodeString(_calcModePath); |
| switch (calcMode) { |
| case "autoNoTable": |
| return ExcelCalcMode.AutomaticNoTable; |
| case "manual": |
| return ExcelCalcMode.Manual; |
| default: |
| return ExcelCalcMode.Automatic; |
| } |
| } |
| set { |
| switch (value) { |
| case ExcelCalcMode.AutomaticNoTable: |
| SetXmlNodeString(_calcModePath, "autoNoTable"); |
| break; |
| case ExcelCalcMode.Manual: |
| SetXmlNodeString(_calcModePath, "manual"); |
| break; |
| default: |
| SetXmlNodeString(_calcModePath, "auto"); |
| break; |
| } |
| } |
| } |
| |
| private const string _fullCalcOnLoadPath = "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 => GetXmlNodeBool(_fullCalcOnLoadPath); |
| set => SetXmlNodeBool(_fullCalcOnLoadPath, value); |
| } |
| |
| internal void Save() { |
| if (Worksheets.Count == 0) { |
| throw new InvalidOperationException("The workbook must contain at least one worksheet"); |
| } |
| |
| DeleteCalcChain(); |
| UpdateDefinedNamesXml(); |
| |
| // save the style sheet |
| Styles.UpdateXml(); |
| |
| // 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(); |
| } |
| |
| _package.Package.CreatePart( |
| SharedStringsUri, |
| ExcelPackage._contentTypeSharedString, |
| SaveSharedStringHandler); |
| Part.CreateRelationship( |
| UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), |
| TargetMode.Internal, |
| ExcelPackage._schemaRelationships + "/sharedStrings"); |
| |
| // 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 Part.GetRelationships()) { |
| if (relationship.TargetUri == calcChain) { |
| Part.DeleteRelationship(relationship.Id); |
| break; |
| } |
| } |
| // delete the calcChain part |
| _package.Package.DeletePart(uriCalcChain); |
| } |
| } |
| |
| private void ValidateDataValidations() { |
| foreach (var sheet in 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(); |
| } |
| } |
| cache.Append("</sst>"); |
| sw.Write(cache.ToString()); |
| sw.Flush(); |
| Part.CreateRelationship( |
| UriHelper.GetRelativeUri(WorkbookUri, SharedStringsUri), |
| 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; |
| } |
| 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("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 time) { |
| elem.InnerText = time.ToOADate().ToString(CultureInfo.InvariantCulture); |
| } else { |
| elem.InnerText = "\"" + name.NameValue + "\""; |
| } |
| } 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; |
| } |
| |
| 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), |
| 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(); |
| |
| //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 rIdExtRef = book.GetAttribute("r:id"); |
| var relExtRef = part.GetRelationship(rIdExtRef); |
| if (relExtRef != null) { |
| _externalReferences.Add(relExtRef.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; |
| } |
| } |
| } |
| } |
| } |
| } |