NanoXLSX.Reader 3.2.1
Loading...
Searching...
No Matches
Internal/Readers/WorkbookReader.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 System;
9using System.IO;
10using System.Xml;
11using NanoXLSX.Exceptions;
12using NanoXLSX.Interfaces;
13using NanoXLSX.Interfaces.Reader;
14using NanoXLSX.Registry;
15using NanoXLSX.Registry.Attributes;
16using NanoXLSX.Utils;
17using NanoXLSX.Utils.Xml;
18using static NanoXLSX.Enums.Password;
19using IOException = NanoXLSX.Exceptions.IOException;
20
22{
26 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.WorkbookReader)]
27 public partial class WorkbookReader : IDocumentReader
28 {
29 private Stream stream;
30 private IPasswordReader passwordReader;
31 // private ReaderOptions readerOptions;
32
33 #region properties
37 public virtual string DocumentType { get { return @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; } }
38
42 public Workbook Workbook { get; set; }
46 public IOptions Options { get; set; }
50 public Action<Stream, Workbook, string, IOptions, int?> InlinePluginHandler { get; set; }
51 #endregion
52
53 #region constructors
57 internal WorkbookReader()
58 {
59 }
60 #endregion
61
62 #region methods
70 public void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action<Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
71 {
72 this.stream = stream;
73 this.Workbook = workbook;
74 this.Options = readerOptions;
75 this.InlinePluginHandler = inlinePluginHandler;
76 this.passwordReader = PlugInLoader.GetPlugIn<IPasswordReader>(PlugInUUID.PasswordReader, new LegacyPasswordReader());
77 this.passwordReader.Init(PasswordType.WorkbookProtection, (ReaderOptions)readerOptions);
78 }
79
84 public void Execute()
85 {
86 try
87 {
88 using (stream) // Close after processing
89 {
90 using (XmlReader reader = XmlReader.Create(stream, XmlStreamUtils.CreateSettings()))
91 {
92 while (reader.Read())
93 {
94 if (reader.NodeType != XmlNodeType.Element)
95 {
96 continue;
97 }
98 if (XmlStreamUtils.IsElement(reader, "sheets"))
99 {
100 GetWorksheetInformation(reader);
101 }
102 else if (XmlStreamUtils.IsElement(reader, "bookViews"))
103 {
104 GetViewInformation(reader);
105 }
106 else if (XmlStreamUtils.IsElement(reader, "workbookProtection"))
107 {
108 GetProtectionInformation(reader);
109 }
110 else if (XmlStreamUtils.IsElement(reader, "definedNames"))
111 {
112 GetDefinedNamesInformation(reader);
113 }
114 }
115 InlinePluginHandler?.Invoke(stream, Workbook, PlugInUUID.WorkbookInlineReader, Options, null);
116 }
117 }
118 }
119 catch (NotSupportedContentException)
120 {
121 throw; // rethrow
122 }
123 catch (Exception ex)
124 {
125 throw new IOException("The XML entry could not be read from the input stream. Please see the inner exception:", ex);
126 }
127 }
128
135 private void GetProtectionInformation(XmlReader reader)
136 {
137 bool lockStructure = false;
138 bool lockWindows = false;
139 string attribute = reader.GetAttribute("lockWindows");
140 if (attribute != null)
141 {
142 lockWindows = ParserUtils.ParseBinaryBool(attribute) == 1;
143 }
144 attribute = reader.GetAttribute("lockStructure");
145 if (attribute != null)
146 {
147 lockStructure = ParserUtils.ParseBinaryBool(attribute) == 1;
148 }
149 Workbook.SetWorkbookProtection(true, lockWindows, lockStructure, null);
150 string outerXml;
151 using (XmlReader subtree = reader.ReadSubtree())
152 {
153 subtree.MoveToContent();
154 outerXml = subtree.ReadOuterXml();
155 }
156 XmlDocument miniDoc = new XmlDocument { XmlResolver = null };
157 miniDoc.LoadXml(outerXml);
158 passwordReader.ReadXmlAttributes(miniDoc.DocumentElement);
159 if (passwordReader.PasswordIsSet())
160 {
161 Workbook.WorkbookProtectionPassword.CopyFrom(passwordReader);
162 }
163 }
164
171 private void GetDefinedNamesInformation(XmlReader reader)
172 {
173 int index = 0;
174 using (XmlReader subtree = reader.ReadSubtree())
175 {
176 subtree.Read(); // consume the definedNames open tag
177 while (subtree.Read())
178 {
179 if (!XmlStreamUtils.IsElement(subtree, "definedName"))
180 {
181 continue;
182 }
183 DefinedNameDefinition definition = new DefinedNameDefinition
184 {
185 Name = subtree.GetAttribute("name"),
186 Comment = subtree.GetAttribute("comment")
187 };
188 string localSheetId = subtree.GetAttribute("localSheetId");
189 if (!string.IsNullOrEmpty(localSheetId))
190 {
191 definition.LocalSheetId = ParserUtils.ParseInt(localSheetId);
192 }
193 definition.Reference = ReadDefinedNameReference(subtree);
194 Workbook.AuxiliaryData.SetData(PlugInUUID.WorkbookReader, PlugInUUID.DefinedNameEntity, index, definition);
195 index++;
196 }
197 }
198 }
199
207 internal static string ReadDefinedNameReference(XmlReader reader)
208 {
209 if (reader.IsEmptyElement)
210 {
211 return string.Empty;
212 }
213 System.Text.StringBuilder sb = new System.Text.StringBuilder();
214 while (reader.Read())
215 {
216 if (reader.NodeType == XmlNodeType.EndElement)
217 {
218 break;
219 }
220 if (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA || reader.NodeType == XmlNodeType.SignificantWhitespace)
221 {
222 sb.Append(reader.Value);
223 }
224 }
225 return sb.ToString();
226 }
227
232 private void GetViewInformation(XmlReader reader)
233 {
234 using (XmlReader subtree = reader.ReadSubtree())
235 {
236 subtree.Read(); // consume the bookViews open tag
237 while (subtree.Read())
238 {
239 if (!XmlStreamUtils.IsElement(subtree, "workbookView"))
240 {
241 continue;
242 }
243 string attribute = subtree.GetAttribute("visibility");
244 if (attribute != null && ParserUtils.ToLower(attribute) == "hidden")
245 {
246 Workbook.Hidden = true;
247 }
248 attribute = subtree.GetAttribute("activeTab");
249 if (!string.IsNullOrEmpty(attribute))
250 {
251 Workbook.AuxiliaryData.SetData(PlugInUUID.WorkbookReader, PlugInUUID.SelectedWorksheetEntity, ParserUtils.ParseInt(attribute));
252 }
253 }
254 }
255 }
256
261 private void GetWorksheetInformation(XmlReader reader)
262 {
263 int visibleWorksheetOrder = 0;
264 using (XmlReader subtree = reader.ReadSubtree())
265 {
266 subtree.Read(); // consume the sheets open tag
267 while (subtree.Read())
268 {
269 if (!XmlStreamUtils.IsElement(subtree, "sheet"))
270 {
271 continue;
272 }
273 try
274 {
275 string sheetName = subtree.GetAttribute("name") ?? "worksheet1";
276 int id = ParserUtils.ParseInt(subtree.GetAttribute("sheetId")); // null will rightly throw
277 string relId = subtree.GetAttribute("r:id");
278 string state = subtree.GetAttribute("state");
279 bool hidden = state != null && ParserUtils.ToLower(state) == "hidden";
280 WorksheetDefinition definition = new WorksheetDefinition(id, sheetName, relId)
281 {
282 Hidden = hidden
283 };
284 Workbook.AuxiliaryData.SetData(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity, visibleWorksheetOrder, definition);
285 visibleWorksheetOrder++;
286 }
287 catch (Exception e)
288 {
289 throw new IOException("The workbook information could not be resolved. Please see the inner exception:", e);
290 }
291 }
292 }
293 }
294 #endregion
295 }
296}
Class representing a reader for legacy passwords.
Class representing a reader to decompile a workbook in an XLSX files.
Workbook Workbook
Workbook reference where read data is stored (should not be null).
void Execute()
Method to execute the main logic of the plug-in (interface implementation).
void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action< Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
Initialization method (interface implementation).
Action< Stream, Workbook, string, IOptions, int?> InlinePluginHandler
Reference to the ReaderPlugInHandler, to be used for post operations in the Execute method.
virtual string DocumentType
Gets the relationship type URI of a workbook document.
Exceptions.IOException IOException