NanoXLSX.Writer 3.2.1
Loading...
Searching...
No Matches
WorkbookWriter.cs
1/*
2 * NanoXLSX is a small .NET library to generate and read XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3 * Copyright Raphael Stoeckli © 2026
4 * This library is licensed under the MIT License.
5 * You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6 */
7
8using NanoXLSX.Interfaces;
9using NanoXLSX.Interfaces.Writer;
10using NanoXLSX.Registry;
11using NanoXLSX.Registry.Attributes;
12using NanoXLSX.Themes;
13using NanoXLSX.Utils;
14using NanoXLSX.Utils.Xml;
15using static NanoXLSX.Enums.Password;
16
18{
22 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.WorkbookWriter)]
23 internal class WorkbookWriter : IPluginWriter
24 {
25 private XmlElement workbook;
26 private IPasswordWriter passwordWriter;
27
28 #region properties
32 public Workbook Workbook { get; set; }
33
37 public XmlElement XmlElement => workbook;
38
39 #endregion
40 #region constructors
44 internal WorkbookWriter()
45 {
46 }
47
48 #endregion
49 #region methods
54 public void Init(IBaseWriter baseWriter)
55 {
56 this.Workbook = baseWriter.Workbook;
57 IPassword passwordInstance = Workbook.WorkbookProtectionPassword;
58 this.passwordWriter = PlugInLoader.GetPlugIn<IPasswordWriter>(PlugInUUID.PasswordWriter, new LegacyPasswordWriter());
59 this.passwordWriter.Init(PasswordType.WorkbookProtection, passwordInstance.PasswordHash);
60 }
61
65 public void Execute()
66 {
67 Workbook wb = Workbook;
68 workbook = XmlElement.CreateElement("workbook");
69 workbook.AddDefaultXmlNameSpace("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
70 workbook.AddNameSpaceAttribute("r", "xmlns", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
71 XmlElement workbookPr = workbook.AddChildElement("workbookPr");
72 if (wb.WorkbookTheme != null)
73 {
74 workbookPr.AddAttribute("defaultThemeVersion", Theme.DefaultThemeVersion);
75 // TODO: add further workbook properties here
76 }
77 if (wb.SelectedWorksheet > 0 || wb.Hidden || HasPaneSplitting())
78 {
79 XmlElement bookViews = workbook.AddChildElement("bookViews");
80 XmlElement workbookView = bookViews.AddChildElement("workbookView");
81 if (wb.Hidden)
82 {
83 workbookView.AddAttribute("visibility", "hidden");
84 }
85 else
86 {
87 workbookView.AddAttribute("activeTab", ParserUtils.ToString(wb.SelectedWorksheet));
88 }
89 }
90 workbook.AddChildElement(GetWorkbookProtectionElement());
91 XmlElement sheets = workbook.AddChildElement("sheets");
92 if (wb.Worksheets.Count > 0)
93 {
94 foreach (Worksheet item in wb.Worksheets)
95 {
96 XmlElement sheet = sheets.AddChildElementWithAttribute("sheet", "id", "rId" + ParserUtils.ToString(item.SheetID), "", "r");
97 sheet.AddAttribute("sheetId", item.SheetID.ToString());
98 sheet.AddAttribute("name", XmlUtils.SanitizeXmlValue(item.SheetName));
99 if (item.Hidden)
100 {
101 sheet.AddAttribute("state", "hidden");
102 }
103 }
104 }
105 else
106 {
107 // Fallback on empty workbook
108 XmlElement sheet = sheets.AddChildElementWithAttribute("sheet", "id", "rId1", "", "r");
109 sheet.AddAttribute("sheetId", "1");
110 sheet.AddAttribute("name", "sheet1");
111 }
112 workbook.AddChildElement(GetDefinedNamesElement());
113
114 WriterPlugInHandler.HandleInlineQueuePlugins(ref workbook, Workbook, PlugInUUID.WorkbookInlineWriter);
115 }
116
121 private XmlElement GetDefinedNamesElement()
122 {
123 System.Collections.Generic.IReadOnlyList<DefinedName> names = Workbook.GetDefinedNames();
124 if (names.Count == 0)
125 {
126 return null;
127 }
128 XmlElement definedNames = XmlElement.CreateElement("definedNames");
129 foreach (DefinedName item in names)
130 {
131 XmlElement definedName = definedNames.AddChildElementWithValue("definedName", XmlUtils.SanitizeXmlValue(GetDefinedNameText(item)));
132 definedName.AddAttribute("name", XmlUtils.SanitizeXmlValue(item.Name));
133 if (item.LocalSheet != null)
134 {
135 int index = Workbook.Worksheets.IndexOf(item.LocalSheet);
136 if (index >= 0)
137 {
138 definedName.AddAttribute("localSheetId", ParserUtils.ToString(index));
139 }
140 }
141 if (!string.IsNullOrEmpty(item.Comment))
142 {
143 definedName.AddAttribute("comment", XmlUtils.SanitizeXmlValue(item.Comment));
144 }
145 }
146 return definedNames;
147 }
148
149 private static string GetDefinedNameText(DefinedName definedName)
150 {
151 if ((definedName.Type == DefinedName.NameType.Cell || definedName.Type == DefinedName.NameType.Range)
152 && definedName.TargetWorksheet != null)
153 {
154 string worksheetName = definedName.TargetWorksheet.SheetName.Replace("'", "''");
155 return "'" + worksheetName + "'!" + definedName.TextValue;
156 }
157 if (definedName.Type == DefinedName.NameType.Constant && definedName.Value is string stringValue)
158 {
159 return "\"" + stringValue.Replace("\"", "\"\"") + "\"";
160 }
161 return definedName.TextValue;
162 }
163
168 private XmlElement GetWorkbookProtectionElement()
169 {
170 Workbook workbook = Workbook;
171 XmlElement workbookProtection = null;
172 if (workbook.UseWorkbookProtection)
173 {
174 workbookProtection = XmlElement.CreateElement("workbookProtection");
175 if (workbook.LockWindowsIfProtected)
176 {
177 workbookProtection.AddAttribute("lockWindows", "1");
178 }
179 if (workbook.LockStructureIfProtected)
180 {
181 workbookProtection.AddAttribute("lockStructure", "1");
182 }
183 if (passwordWriter.PasswordIsSet())
184 {
185 workbookProtection.AddAttributes(passwordWriter.GetAttributes());
186 }
187 }
188 return workbookProtection;
189 }
190
195 private bool HasPaneSplitting()
196 {
197 foreach (Worksheet worksheet in Workbook.Worksheets)
198 {
199 if (worksheet.PaneSplitLeftWidth != null || worksheet.PaneSplitTopHeight != null || worksheet.PaneSplitAddress != null)
200 {
201 return true;
202 }
203 }
204 return false;
205 }
206
207 #endregion
208
209 }
210}