blob: a0e4ebbaa19fe333f95bb40893d254d8d77f33e7 [file] [log] [blame]
/*******************************************************************************
* 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 2009-10-01
* Jan Källman License changed GPL-->LGPL 2011-12-27
*******************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using OfficeOpenXml.Utils;
namespace OfficeOpenXml;
/// <summary>
/// The collection of worksheets for the workbook
/// </summary>
public class ExcelWorksheets : XmlHelper, IEnumerable<ExcelWorksheet> {
private readonly ExcelPackage _pck;
private readonly ExcelWorkbook _workbook;
private Dictionary<int, ExcelWorksheet> _worksheets = new();
private readonly XmlNamespaceManager _namespaceManager;
internal ExcelWorksheets(
ExcelPackage pck,
ExcelWorkbook workbook,
XmlNamespaceManager nsm,
XmlNode topNode)
: base(nsm, topNode) {
_pck = pck;
_workbook = workbook;
_namespaceManager = nsm;
int positionId = 1;
foreach (XmlNode sheetNode in topNode.ChildNodes) {
if (sheetNode.NodeType == XmlNodeType.Element) {
string name = sheetNode.Attributes["name"].Value;
//Get the relationship id
string relId = sheetNode.Attributes["r:id"].Value;
int sheetId = Convert.ToInt32(sheetNode.Attributes["sheetId"].Value);
//Hidden property
eWorkSheetHidden hidden = eWorkSheetHidden.Visible;
XmlNode attr = sheetNode.Attributes["state"];
if (attr != null) {
hidden = TranslateHidden(attr.Value);
}
var sheetRelation = _workbook.Part.GetRelationship(relId);
Uri uriWorksheet = UriHelper.ResolvePartUri(
ExcelWorkbook.WorkbookUri,
sheetRelation.TargetUri);
//add the worksheet
if (sheetRelation.RelationshipType.EndsWith("chartsheet")) {
_worksheets.Add(
positionId,
new ExcelChartsheet(
_namespaceManager,
pck,
_workbook,
uriWorksheet,
name,
sheetId,
positionId,
hidden));
} else {
_worksheets.Add(
positionId,
new(
_namespaceManager,
pck,
_workbook,
uriWorksheet,
name,
sheetId,
positionId,
hidden));
}
positionId++;
}
}
}
private eWorkSheetHidden TranslateHidden(string value) {
switch (value) {
case "hidden":
return eWorkSheetHidden.Hidden;
case "veryHidden":
return eWorkSheetHidden.VeryHidden;
default:
return eWorkSheetHidden.Visible;
}
}
/// <summary>
/// Returns the number of worksheets in the workbook
/// </summary>
public int Count => (_worksheets.Count);
private const string _errDupWorksheet =
"A worksheet with this name already exists in the workbook";
internal const string _worksheetContentType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
/// <summary>
/// Foreach support
/// </summary>
/// <returns>An enumerator</returns>
public IEnumerator<ExcelWorksheet> GetEnumerator() {
return (_worksheets.Values.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator() {
return (_worksheets.Values.GetEnumerator());
}
/// <summary>
/// Adds a new blank worksheet.
/// </summary>
/// <param name="name">The name of the workbook</param>
public ExcelWorksheet Add(string name) {
name = ValidateFixSheetName(name);
if (GetByName(name) != null) {
throw (new InvalidOperationException(_errDupWorksheet + " : " + name));
}
GetSheetUri(ref name, out var sheetId, out var uriWorksheet, false);
// Create the new worksheet
var rel = _pck.CreateXmlDocument(
uriWorksheet,
_worksheetContentType,
ExcelPackage._schemaRelationships + "/worksheet",
CreateNewWorksheet(false));
// Add worksheet to the workbook
XmlElement worksheetNode = _workbook.WorkbookXml.CreateElement(
"sheet",
ExcelPackage._schemaMain);
worksheetNode.SetAttribute("name", name);
worksheetNode.SetAttribute("sheetId", sheetId.ToString());
worksheetNode.SetAttribute("id", ExcelPackage._schemaRelationships, rel.Id);
TopNode.AppendChild(worksheetNode);
int positionId = _worksheets.Count + 1;
ExcelWorksheet worksheet = new(
_namespaceManager,
_pck,
_workbook,
uriWorksheet,
name,
sheetId,
positionId,
eWorkSheetHidden.Visible);
_worksheets.Add(positionId, worksheet);
return worksheet;
}
private void GetSheetUri(ref string name, out int sheetId, out Uri uriWorksheet, bool isChart) {
name = ValidateFixSheetName(name);
//First find maximum existing sheetID
sheetId = 0;
foreach (var ws in this) {
if (ws.SheetID > sheetId) {
sheetId = ws.SheetID;
}
}
// we now have the max existing values, so add one
sheetId++;
// add the new worksheet to the package
if (isChart) {
uriWorksheet = new("/xl/chartsheets/chartsheet" + sheetId + ".xml", UriKind.Relative);
} else {
uriWorksheet = new("/xl/worksheets/sheet" + sheetId + ".xml", UriKind.Relative);
}
}
internal string ValidateFixSheetName(string name) {
//remove invalid characters
if (ValidateName(name)) {
if (name.IndexOf(':') > -1) {
name = name.Replace(":", " ");
}
if (name.IndexOf('/') > -1) {
name = name.Replace("/", " ");
}
if (name.IndexOf('\\') > -1) {
name = name.Replace("\\", " ");
}
if (name.IndexOf('?') > -1) {
name = name.Replace("?", " ");
}
if (name.IndexOf('[') > -1) {
name = name.Replace("[", " ");
}
if (name.IndexOf(']') > -1) {
name = name.Replace("]", " ");
}
}
if (name.Trim() == "") {
throw new ArgumentException("The worksheet can not have an empty name");
}
if (name.Length > 31) {
name = name.Substring(0, 31); //A sheet can have max 31 char's
}
return name;
}
/// <summary>
/// Validate the sheetname
/// </summary>
/// <param name="name">The Name</param>
/// <returns>True if valid</returns>
private bool ValidateName(string name) {
return Regex.IsMatch(name, @":|\?|/|\\|\[|\]");
}
/// <summary>
/// Creates the XML document representing a new empty worksheet
/// </summary>
/// <returns></returns>
internal XmlDocument CreateNewWorksheet(bool isChart) {
XmlDocument xmlDoc = new XmlDocument();
XmlElement elemWs = xmlDoc.CreateElement(
isChart ? "chartsheet" : "worksheet",
ExcelPackage._schemaMain);
elemWs.SetAttribute("xmlns:r", ExcelPackage._schemaRelationships);
xmlDoc.AppendChild(elemWs);
if (isChart) {
XmlElement elemSheetPr = xmlDoc.CreateElement("sheetPr", ExcelPackage._schemaMain);
elemWs.AppendChild(elemSheetPr);
XmlElement elemSheetViews = xmlDoc.CreateElement("sheetViews", ExcelPackage._schemaMain);
elemWs.AppendChild(elemSheetViews);
XmlElement elemSheetView = xmlDoc.CreateElement("sheetView", ExcelPackage._schemaMain);
elemSheetView.SetAttribute("workbookViewId", "0");
elemSheetView.SetAttribute("zoomToFit", "1");
elemSheetViews.AppendChild(elemSheetView);
} else {
XmlElement elemSheetViews = xmlDoc.CreateElement("sheetViews", ExcelPackage._schemaMain);
elemWs.AppendChild(elemSheetViews);
XmlElement elemSheetView = xmlDoc.CreateElement("sheetView", ExcelPackage._schemaMain);
elemSheetView.SetAttribute("workbookViewId", "0");
elemSheetViews.AppendChild(elemSheetView);
XmlElement elemSheetFormatPr = xmlDoc.CreateElement(
"sheetFormatPr",
ExcelPackage._schemaMain);
elemSheetFormatPr.SetAttribute("defaultRowHeight", "15");
elemWs.AppendChild(elemSheetFormatPr);
XmlElement elemSheetData = xmlDoc.CreateElement("sheetData", ExcelPackage._schemaMain);
elemWs.AppendChild(elemSheetData);
}
return xmlDoc;
}
/// <summary>
/// Returns the worksheet at the specified position.
/// </summary>
/// <param name="positionId">The position of the worksheet. 1-base</param>
/// <returns></returns>
public ExcelWorksheet this[int positionId] {
get {
if (_worksheets.ContainsKey(positionId)) {
return _worksheets[positionId];
}
throw (new IndexOutOfRangeException("Worksheet position out of range."));
}
}
/// <summary>
/// Returns the worksheet matching the specified name
/// </summary>
/// <param name="name">The name of the worksheet</param>
/// <returns></returns>
public ExcelWorksheet this[string name] => GetByName(name);
internal ExcelWorksheet GetBySheetId(int localSheetId) {
foreach (ExcelWorksheet ws in this) {
if (ws.SheetID == localSheetId) {
return ws;
}
}
return null;
}
private ExcelWorksheet GetByName(string name) {
if (string.IsNullOrEmpty(name)) {
return null;
}
ExcelWorksheet xlWorksheet = null;
foreach (ExcelWorksheet worksheet in _worksheets.Values) {
if (worksheet.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) {
xlWorksheet = worksheet;
}
}
return (xlWorksheet);
}
} // end class Worksheets