NanoXLSX.Reader 3.2.1
Loading...
Searching...
No Matches
SharedStringsReader.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.Collections.Generic;
10using System.IO;
11using System.Text;
12using System.Xml;
13using NanoXLSX.Interfaces;
14using NanoXLSX.Interfaces.Reader;
15using NanoXLSX.Registry;
16using NanoXLSX.Registry.Attributes;
17using NanoXLSX.Utils;
18using NanoXLSX.Utils.Xml;
19using IOException = NanoXLSX.Exceptions.IOException;
20
22{
26 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.SharedStringsReader)]
27 public class SharedStringsReader : ISharedStringReader
28 {
29
30 #region privateFields
31 private bool capturePhoneticCharacters;
32 private readonly List<PhoneticInfo> phoneticsInfo;
33 private Stream stream;
34 #endregion
35
36 #region properties
37
41 public virtual string DocumentType { get { return @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"; } }
42
49 public List<string> SharedStrings { get; private set; }
50
54 public Workbook Workbook { get; set; }
58 public IOptions Options { get; set; }
62 public Action<Stream, Workbook, string, IOptions, int?> InlinePluginHandler { get; set; }
63 #endregion
64
65 #region constructors
70 {
71 phoneticsInfo = new List<PhoneticInfo>();
72 SharedStrings = new List<string>();
73 }
74 #endregion
75
76 #region methods
84 public void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action<Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
85 {
86 this.stream = stream;
87 this.Workbook = workbook;
88 this.Options = readerOptions;
89 this.InlinePluginHandler = inlinePluginHandler;
90 if (readerOptions is ReaderOptions options)
91 {
92 this.capturePhoneticCharacters = options.EnforcePhoneticCharacterImport;
93 }
94 }
95
100 public void Execute()
101 {
102 try
103 {
104 using (stream) // Close after processing
105 {
106 StringBuilder sb = new StringBuilder();
107 using (XmlReader reader = XmlReader.Create(stream, XmlStreamUtils.CreateSettings()))
108 {
109 while (reader.Read())
110 {
111 if (!XmlStreamUtils.IsElement(reader, "si"))
112 {
113 continue;
114 }
115 sb.Clear();
116 ReadSiElement(reader, sb);
117 if (capturePhoneticCharacters)
118 {
119 SharedStrings.Add(ProcessPhoneticCharacters(sb));
120 }
121 else
122 {
123 SharedStrings.Add(sb.ToString());
124 }
125 }
126 InlinePluginHandler?.Invoke(stream, Workbook, PlugInUUID.SharedStringsInlineReader, Options, null);
127 }
128 }
129 }
130 catch (Exception ex)
131 {
132 throw new IOException("The XML entry could not be read from the " + nameof(stream) + ". Please see the inner exception:", ex);
133 }
134 }
135
140 private void ReadSiElement(XmlReader reader, StringBuilder sb)
141 {
142 using (XmlReader siSubtree = reader.ReadSubtree())
143 {
144 siSubtree.Read(); // consume the <si> open tag
145 while (siSubtree.Read())
146 {
147 if (siSubtree.NodeType != XmlNodeType.Element)
148 {
149 continue;
150 }
151 if (siSubtree.LocalName.Equals("rPh", StringComparison.OrdinalIgnoreCase))
152 {
153 if (capturePhoneticCharacters)
154 {
155 ReadPhoneticElement(siSubtree);
156 }
157 else
158 {
159 using (siSubtree.ReadSubtree()) { } // dispose immediately; positions siSubtree at </rPh>
160 }
161 }
162 else if (siSubtree.LocalName.Equals("t", StringComparison.OrdinalIgnoreCase))
163 {
164 string text;
165 using (XmlReader tSubtree = siSubtree.ReadSubtree())
166 {
167 tSubtree.Read(); // position at <t>
168 text = tSubtree.ReadElementContentAsString();
169 }
170 if (!string.IsNullOrEmpty(text))
171 {
172 sb.Append(text);
173 }
174 }
175 }
176 }
177 }
178
183 private void ReadPhoneticElement(XmlReader reader)
184 {
185 string start = reader.GetAttribute("sb");
186 string end = reader.GetAttribute("eb");
187 string text = null;
188 using (XmlReader rPhSubtree = reader.ReadSubtree())
189 {
190 rPhSubtree.Read(); // consume the <rPh> open tag
191 while (rPhSubtree.Read())
192 {
193 if (rPhSubtree.NodeType == XmlNodeType.Element
194 && rPhSubtree.LocalName.Equals("t", StringComparison.OrdinalIgnoreCase))
195 {
196 text = rPhSubtree.ReadElementContentAsString();
197 }
198 }
199 }
200 if (!string.IsNullOrEmpty(text))
201 {
202 phoneticsInfo.Add(new PhoneticInfo(text, start, end));
203 }
204 }
205
211 private string ProcessPhoneticCharacters(StringBuilder sb)
212 {
213 if (phoneticsInfo.Count == 0)
214 {
215 return sb.ToString();
216 }
217 string text = sb.ToString();
218 StringBuilder sb2 = new StringBuilder();
219 int currentTextIndex = 0;
220 foreach (PhoneticInfo info in phoneticsInfo)
221 {
222 sb2.Append(text.Substring(currentTextIndex, info.StartIndex + info.Length - currentTextIndex));
223 sb2.Append('(').Append(info.Value).Append(')');
224 currentTextIndex = info.StartIndex + info.Length;
225 }
226 sb2.Append(text.Substring(currentTextIndex));
227
228 phoneticsInfo.Clear();
229 return sb2.ToString();
230 }
231
232
233 #endregion
234
235 #region sub-classes
240 sealed class PhoneticInfo
241 {
245 public string Value { get; private set; }
249 public int StartIndex { get; private set; }
253 public int Length { get; private set; }
254
261 public PhoneticInfo(string value, string start, string end)
262 {
263 Value = value;
264 StartIndex = ParserUtils.ParseInt(start);
265 Length = ParserUtils.ParseInt(end) - StartIndex;
266
267 }
268 }
269 #endregion
270 }
271}
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 shared strings document.
Workbook Workbook
Workbook reference where read data is stored (should not be null).
List< string > SharedStrings
List of shared string entries.
void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action< Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
Initialization method (interface implementation).
SharedStringsReader()
Default constructor - Must be defined for instantiation of the plug-ins.
void Execute()
Method to execute the main logic of the plug-in (interface implementation).
Exceptions.IOException IOException