SmlDataRetriever.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. // Copyright (c) Microsoft. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Drawing;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Xml.Linq;
  11. using DocumentFormat.OpenXml.Packaging;
  12. using System.IO;
  13. using OpenXmlPowerTools;
  14. namespace OpenXmlPowerTools
  15. {
  16. public class SmlDataRetriever
  17. {
  18. public static XElement RetrieveSheet(SmlDocument smlDoc, string sheetName)
  19. {
  20. using (MemoryStream ms = new MemoryStream())
  21. {
  22. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  23. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  24. {
  25. return RetrieveSheet(sDoc, sheetName);
  26. }
  27. }
  28. }
  29. public static XElement RetrieveSheet(string fileName, string sheetName)
  30. {
  31. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  32. {
  33. return RetrieveSheet(sDoc, sheetName);
  34. }
  35. }
  36. public static XElement RetrieveSheet(SpreadsheetDocument sDoc, string sheetName)
  37. {
  38. var wbXdoc = sDoc.WorkbookPart.GetXDocument();
  39. var sheet = wbXdoc
  40. .Root
  41. .Elements(S.sheets)
  42. .Elements(S.sheet)
  43. .FirstOrDefault(s => (string)s.Attribute("name") == sheetName);
  44. if (sheet == null)
  45. throw new ArgumentException("Invalid sheet name passed to RetrieveSheet", "sheetName");
  46. var range = "A1:XFD1048576";
  47. int leftColumn, topRow, rightColumn, bottomRow;
  48. XlsxTables.ParseRange(range, out leftColumn, out topRow, out rightColumn, out bottomRow);
  49. return RetrieveRange(sDoc, sheetName, leftColumn, topRow, rightColumn, bottomRow);
  50. }
  51. public static XElement RetrieveRange(SmlDocument smlDoc, string sheetName, string range)
  52. {
  53. using (MemoryStream ms = new MemoryStream())
  54. {
  55. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  56. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  57. {
  58. return RetrieveRange(sDoc, sheetName, range);
  59. }
  60. }
  61. }
  62. public static XElement RetrieveRange(string fileName, string sheetName, string range)
  63. {
  64. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  65. {
  66. return RetrieveRange(sDoc, sheetName, range);
  67. }
  68. }
  69. public static XElement RetrieveRange(SpreadsheetDocument sDoc, string sheetName, string range)
  70. {
  71. int leftColumn, topRow, rightColumn, bottomRow;
  72. XlsxTables.ParseRange(range, out leftColumn, out topRow, out rightColumn, out bottomRow);
  73. return RetrieveRange(sDoc, sheetName, leftColumn, topRow, rightColumn, bottomRow);
  74. }
  75. public static XElement RetrieveRange(SmlDocument smlDoc, string sheetName, int leftColumn, int topRow, int rightColumn, int bottomRow)
  76. {
  77. using (MemoryStream ms = new MemoryStream())
  78. {
  79. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  80. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  81. {
  82. return RetrieveRange(sDoc, sheetName, leftColumn, topRow, rightColumn, bottomRow);
  83. }
  84. }
  85. }
  86. public static XElement RetrieveRange(string fileName, string sheetName, int leftColumn, int topRow, int rightColumn, int bottomRow)
  87. {
  88. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  89. {
  90. return RetrieveRange(sDoc, sheetName, leftColumn, topRow, rightColumn, bottomRow);
  91. }
  92. }
  93. public static XElement RetrieveRange(SpreadsheetDocument sDoc, string sheetName, int leftColumn, int topRow, int rightColumn, int bottomRow)
  94. {
  95. var wbXdoc = sDoc.WorkbookPart.GetXDocument();
  96. var sheet = wbXdoc
  97. .Root
  98. .Elements(S.sheets)
  99. .Elements(S.sheet)
  100. .FirstOrDefault(s => (string)s.Attribute("name") == sheetName);
  101. if (sheet == null)
  102. throw new ArgumentException("Invalid sheet name passed to RetrieveRange", "sheetName");
  103. var rId = (string)sheet.Attribute(R.id);
  104. if (rId == null)
  105. throw new FileFormatException("Invalid spreadsheet");
  106. var sheetPart = sDoc.WorkbookPart.GetPartById(rId);
  107. if (sheetPart == null)
  108. throw new FileFormatException("Invalid spreadsheet");
  109. var shXDoc = sheetPart.GetXDocument();
  110. if (sDoc.WorkbookPart.WorkbookStylesPart == null)
  111. throw new FileFormatException("Invalid spreadsheet. No WorkbookStylesPart.");
  112. var styleXDoc = sDoc.WorkbookPart.WorkbookStylesPart.GetXDocument();
  113. // if there is no shared string table, sharedStringTable will be null
  114. // it will only be used if there is a cell type == "s", in which case, referencing this
  115. // part would indicate an invalid spreadsheet.
  116. SharedStringTablePart sharedStringTable = sDoc.WorkbookPart.SharedStringTablePart;
  117. FixUpCellsThatHaveNoRAtt(shXDoc);
  118. // assemble the transform
  119. var sheetData = shXDoc
  120. .Root
  121. .Elements(S.sheetData)
  122. .Elements(S.row)
  123. .Select(row =>
  124. {
  125. // filter
  126. string ra = (string)row.Attribute("r");
  127. if (ra == null)
  128. return null;
  129. int rowNbr;
  130. if (!int.TryParse(ra, out rowNbr))
  131. return null;
  132. if (rowNbr < topRow)
  133. return null;
  134. if (rowNbr > bottomRow)
  135. return null;
  136. var cells = row
  137. .Elements(S.c)
  138. .Select(cell =>
  139. {
  140. var cellAddress = (string)cell.Attribute("r");
  141. if (cellAddress == null)
  142. throw new FileFormatException("Invalid spreadsheet - cell does not have r attribute.");
  143. var splitCellAddress = XlsxTables.SplitAddress(cellAddress);
  144. var columnAddress = splitCellAddress[0];
  145. var columnIndex = XlsxTables.ColumnAddressToIndex(columnAddress);
  146. // filter
  147. if (columnIndex < leftColumn || columnIndex > rightColumn)
  148. return null;
  149. var cellType = (string)cell.Attribute("t");
  150. string sharedString = null;
  151. if (cellType == "s")
  152. {
  153. int sharedStringIndex;
  154. string sharedStringBeforeParsing = (string)cell.Element(S.v);
  155. if (sharedStringBeforeParsing == null)
  156. sharedStringBeforeParsing = (string)cell.Elements(S._is).Elements(S.t).FirstOrDefault();
  157. if (sharedStringBeforeParsing == null)
  158. throw new FileFormatException("Invalid document");
  159. if (!int.TryParse(sharedStringBeforeParsing, out sharedStringIndex))
  160. throw new FileFormatException("Invalid document");
  161. XElement sharedStringElement = null;
  162. if (sharedStringTable == null)
  163. throw new FileFormatException("Invalid spreadsheet. Shared string, but no Shared String Part.");
  164. sharedStringElement =
  165. sharedStringTable
  166. .GetXDocument()
  167. .Root
  168. .Elements(S.si)
  169. .Skip(sharedStringIndex)
  170. .FirstOrDefault();
  171. if (sharedStringElement == null)
  172. throw new FileFormatException("Invalid spreadsheet. Shared string reference not valid.");
  173. sharedString =
  174. sharedStringElement
  175. .Descendants(S.t)
  176. .StringConcatenate(e => (string)e);
  177. }
  178. if (sharedString != null)
  179. {
  180. XElement cellProps = GetCellProps_NotInTable(sDoc, styleXDoc, cell);
  181. string value = sharedString;
  182. string displayValue;
  183. string color = null;
  184. if (cellProps != null)
  185. displayValue = SmlCellFormatter.FormatCell(
  186. (string)cellProps.Attribute("formatCode"),
  187. value,
  188. out color);
  189. else
  190. displayValue = value;
  191. XElement newCell1 = new XElement("Cell",
  192. new XAttribute("Ref", (string)cell.Attribute("r")),
  193. new XAttribute("ColumnId", columnAddress),
  194. new XAttribute("ColumnNumber", columnIndex),
  195. cell.Attribute("f") != null ? new XAttribute("Formula", (string)cell.Attribute("f")) : null,
  196. cell.Attribute("s") != null ? new XAttribute("Style", (string)cell.Attribute("s")) : null,
  197. cell.Attribute("t") != null ? new XAttribute("Type", (string)cell.Attribute("t")) : null,
  198. cellProps,
  199. new XElement("Value", value),
  200. new XElement("DisplayValue", displayValue),
  201. color != null ? new XElement("DisplayColor", color) : null
  202. );
  203. return newCell1;
  204. }
  205. else
  206. {
  207. var type = (string)cell.Attribute("t");
  208. XElement value = new XElement("Value", cell.Value);
  209. if (type != null && type == "inlineStr")
  210. {
  211. type = "s";
  212. }
  213. XAttribute typeAttr = null;
  214. if (type != null)
  215. typeAttr = new XAttribute("Type", type);
  216. XElement cellProps = GetCellProps_NotInTable(sDoc, styleXDoc, cell);
  217. string displayValue;
  218. string color = null;
  219. if (cellProps != null)
  220. displayValue = SmlCellFormatter.FormatCell(
  221. (string)cellProps.Attribute("formatCode"),
  222. cell.Value,
  223. out color);
  224. else
  225. displayValue = displayValue = SmlCellFormatter.FormatCell(
  226. (string)"General",
  227. cell.Value,
  228. out color);
  229. XElement newCell2 = new XElement("Cell",
  230. new XAttribute("Ref", (string)cell.Attribute("r")),
  231. new XAttribute("ColumnId", columnAddress),
  232. new XAttribute("ColumnNumber", columnIndex),
  233. typeAttr,
  234. cell.Attribute("f") != null ? new XAttribute("Formula", (string)cell.Attribute("f")) : null,
  235. cell.Attribute("s") != null ? new XAttribute("Style", (string)cell.Attribute("s")) : null,
  236. cellProps,
  237. value,
  238. new XElement("DisplayValue", displayValue),
  239. color != null ? new XElement("DisplayColor", color) : null);
  240. return newCell2;
  241. }
  242. });
  243. XElement dataRow = new XElement("Row",
  244. row.Attribute("r") != null ? new XAttribute("RowNumber", (int)row.Attribute("r")) : null,
  245. cells);
  246. return dataRow;
  247. });
  248. var dataProps = GetDataProps(shXDoc);
  249. XElement data = new XElement("Data",
  250. dataProps,
  251. sheetData);
  252. return data;
  253. }
  254. // Sometimes encounter cells that have no r attribute, so infer it if possible.
  255. // These are invalid spreadsheets, but attempt to get the data anyway.
  256. private static void FixUpCellsThatHaveNoRAtt(XDocument shXDoc)
  257. {
  258. // if there are any rows that have all cells with no r attribute, then fix them up
  259. var invalidRows = shXDoc
  260. .Descendants(S.row)
  261. .Where(r => ! r.Elements(S.c).Any(c => c.Attribute("r") != null))
  262. .ToList();
  263. foreach (var row in invalidRows)
  264. {
  265. var rowNumberStr = (string)row.Attribute("r");
  266. var colNumber = 0;
  267. foreach (var cell in row.Elements(S.c))
  268. {
  269. var newCellRef = XlsxTables.IndexToColumnAddress(colNumber) + rowNumberStr;
  270. cell.Add(new XAttribute("r", newCellRef));
  271. }
  272. }
  273. // repeat iteratively until no further fixes can be made
  274. while (true)
  275. {
  276. var invalidCells = shXDoc
  277. .Descendants(S.c)
  278. .Where(c => c.Attribute("r") == null)
  279. .ToList();
  280. bool didFixup = false;
  281. foreach (var cell in invalidCells)
  282. {
  283. var followingCell = cell.ElementsAfterSelf(S.c).FirstOrDefault();
  284. if (followingCell != null)
  285. {
  286. var followingR = (string)followingCell.Attribute("r");
  287. if (followingR != null)
  288. {
  289. var spl = XlsxTables.SplitAddress(followingR);
  290. var colIdxFollowing = XlsxTables.ColumnAddressToIndex(spl[0]);
  291. var newRef = XlsxTables.IndexToColumnAddress(colIdxFollowing - 1) + spl[1];
  292. cell.Add(new XAttribute("r", newRef));
  293. didFixup = true;
  294. }
  295. else
  296. {
  297. didFixup = FixUpBasedOnPrecedingCell(didFixup, cell);
  298. }
  299. }
  300. else
  301. {
  302. didFixup = FixUpBasedOnPrecedingCell(didFixup, cell);
  303. }
  304. }
  305. if (!didFixup)
  306. break;
  307. }
  308. }
  309. private static bool FixUpBasedOnPrecedingCell(bool didFixup, XElement cell)
  310. {
  311. XElement precedingCell = GetPrevousElement(cell);
  312. if (precedingCell != null)
  313. {
  314. var precedingR = (string)precedingCell.Attribute("r");
  315. if (precedingR != null)
  316. {
  317. var spl = XlsxTables.SplitAddress(precedingR);
  318. var colIdxFollowing = XlsxTables.ColumnAddressToIndex(spl[0]);
  319. var newRef = XlsxTables.IndexToColumnAddress(colIdxFollowing + 1) + spl[1];
  320. cell.Add(new XAttribute("r", newRef));
  321. didFixup = true;
  322. }
  323. }
  324. return didFixup;
  325. }
  326. private static XElement GetPrevousElement(XElement element)
  327. {
  328. XElement previousElement = null;
  329. XNode currentNode = element;
  330. while (true)
  331. {
  332. if (currentNode.PreviousNode == null)
  333. return null;
  334. previousElement = currentNode.PreviousNode as XElement;
  335. if (previousElement != null)
  336. return previousElement;
  337. currentNode = currentNode.PreviousNode;
  338. }
  339. }
  340. public static XElement RetrieveTable(SmlDocument smlDoc, string sheetName, string tableName)
  341. {
  342. using (MemoryStream ms = new MemoryStream())
  343. {
  344. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  345. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  346. {
  347. return RetrieveTable(sDoc, tableName);
  348. }
  349. }
  350. }
  351. public static XElement RetrieveTable(string fileName, string tableName)
  352. {
  353. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  354. {
  355. return RetrieveTable(sDoc, tableName);
  356. }
  357. }
  358. public static XElement RetrieveTable(SpreadsheetDocument sDoc, string tableName)
  359. {
  360. var table = sDoc.Table(tableName);
  361. if (table == null)
  362. throw new ArgumentException("Table not found", "tableName");
  363. var styleXDoc = sDoc.WorkbookPart.WorkbookStylesPart.GetXDocument();
  364. var r = table.Ref;
  365. int leftColumn, topRow, rightColumn, bottomRow;
  366. XlsxTables.ParseRange(r, out leftColumn, out topRow, out rightColumn, out bottomRow);
  367. var shXDoc = table.Parent.GetXDocument();
  368. FixUpCellsThatHaveNoRAtt(shXDoc);
  369. // assemble the transform
  370. var columns = new XElement("Columns",
  371. table.TableColumns().Select(tc =>
  372. {
  373. var colXElement = new XElement("Column",
  374. tc.Name != null ? new XAttribute("Name", tc.Name) : null,
  375. tc.UniqueName != null ? new XAttribute("UniqueName", tc.UniqueName) : null,
  376. new XAttribute("ColumnIndex", tc.ColumnIndex),
  377. new XAttribute("Id", tc.Id),
  378. tc.DataDxfId != null ? new XAttribute("DataDxfId", tc.DataDxfId) : null,
  379. tc.QueryTableFieldId != null ? new XAttribute("QueryTableFieldId", tc.QueryTableFieldId) : null);
  380. return colXElement;
  381. }));
  382. var dataProps = GetDataProps(shXDoc);
  383. var data = new XElement("Data",
  384. dataProps,
  385. table.TableRows().Select(tr =>
  386. {
  387. int rowRef;
  388. if (!int.TryParse(tr.Row.RowId, out rowRef))
  389. throw new FileFormatException("Invalid spreadsheet");
  390. // filter
  391. if (rowRef < topRow || rowRef > bottomRow)
  392. return null;
  393. var cellData = tr.Row.Cells().Select(tc =>
  394. {
  395. // filter
  396. var columnIndex = tc.ColumnIndex;
  397. if (columnIndex < leftColumn || columnIndex > rightColumn)
  398. return null;
  399. XElement cellProps = GetCellProps_InTable(sDoc, styleXDoc, table, tc);
  400. if (tc.SharedString != null)
  401. {
  402. string displayValue;
  403. string color = null;
  404. if (cellProps != null)
  405. displayValue = SmlCellFormatter.FormatCell(
  406. (string)cellProps.Attribute("formatCode"),
  407. tc.SharedString,
  408. out color);
  409. else
  410. displayValue = tc.SharedString;
  411. XElement newCell1 = new XElement("Cell",
  412. tc.CellElement != null ? new XAttribute("Ref", (string)tc.CellElement.Attribute("r")) : null,
  413. tc.ColumnAddress != null ? new XAttribute("ColumnId", tc.ColumnAddress) : null,
  414. new XAttribute("ColumnNumber", tc.ColumnIndex),
  415. tc.Type != null ? new XAttribute("Type", "s") : null,
  416. tc.Formula != null ? new XAttribute("Formula", tc.Formula) : null,
  417. tc.Style != null ? new XAttribute("Style", tc.Style) : null,
  418. cellProps,
  419. new XElement("Value", tc.SharedString),
  420. new XElement("DisplayValue", displayValue),
  421. color != null ? new XElement("DisplayColor", color) : null);
  422. return newCell1;
  423. }
  424. else
  425. {
  426. XAttribute type = null;
  427. if (tc.Type != null)
  428. {
  429. if (tc.Type == "inlineStr")
  430. type = new XAttribute("Type", "s");
  431. else
  432. type = new XAttribute("Type", tc.Type);
  433. }
  434. string displayValue;
  435. string color = null;
  436. if (cellProps != null)
  437. displayValue = SmlCellFormatter.FormatCell(
  438. (string)cellProps.Attribute("formatCode"),
  439. tc.Value,
  440. out color);
  441. else
  442. displayValue = SmlCellFormatter.FormatCell(
  443. (string)"General",
  444. tc.Value,
  445. out color);
  446. XElement newCell = new XElement("Cell",
  447. tc.CellElement != null ? new XAttribute("Ref", (string)tc.CellElement.Attribute("r")) : null,
  448. tc.ColumnAddress != null ? new XAttribute("ColumnId", tc.ColumnAddress) : null,
  449. new XAttribute("ColumnNumber", tc.ColumnIndex),
  450. type,
  451. tc.Formula != null ? new XAttribute("Formula", tc.Formula) : null,
  452. tc.Style != null ? new XAttribute("Style", tc.Style) : null,
  453. cellProps,
  454. new XElement("Value", tc.Value),
  455. new XElement("DisplayValue", displayValue),
  456. color != null ? new XElement("DisplayColor", color) : null);
  457. return newCell;
  458. }
  459. });
  460. var rowProps = GetRowProps(tr.Row.RowElement);
  461. var newRow = new XElement("Row",
  462. rowProps,
  463. new XAttribute("RowNumber", tr.Row.RowId),
  464. cellData);
  465. return newRow;
  466. }));
  467. XElement tableProps = GetTableProps(table);
  468. var tableXml = new XElement("Table",
  469. tableProps,
  470. table.TableName != null ? new XAttribute("TableName", table.TableName) : null,
  471. table.DisplayName != null ? new XAttribute("DisplayName", table.DisplayName) : null,
  472. table.Ref != null ? new XAttribute("Ref", table.Ref) : null,
  473. table.HeaderRowCount != null ? new XAttribute("HeaderRowCount", table.HeaderRowCount) : null,
  474. table.TotalsRowCount != null ? new XAttribute("TotalsRowCount", table.TotalsRowCount) : null,
  475. columns,
  476. data);
  477. return tableXml;
  478. }
  479. public static string[] SheetNames(SmlDocument smlDoc)
  480. {
  481. using (MemoryStream ms = new MemoryStream())
  482. {
  483. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  484. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  485. {
  486. return SheetNames(sDoc);
  487. }
  488. }
  489. }
  490. public static string[] SheetNames(string fileName)
  491. {
  492. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  493. {
  494. return SheetNames(sDoc);
  495. }
  496. }
  497. public static string[] SheetNames(SpreadsheetDocument sDoc)
  498. {
  499. var workbookXDoc = sDoc.WorkbookPart.GetXDocument();
  500. var sheetNames = workbookXDoc.Root.Elements(S.sheets).Elements(S.sheet).Attributes("name").Select(a => (string)a).ToArray();
  501. return sheetNames;
  502. }
  503. public static string[] TableNames(SmlDocument smlDoc)
  504. {
  505. using (MemoryStream ms = new MemoryStream())
  506. {
  507. ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
  508. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
  509. {
  510. return TableNames(sDoc);
  511. }
  512. }
  513. }
  514. public static string[] TableNames(string fileName)
  515. {
  516. using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fileName, false))
  517. {
  518. return TableNames(sDoc);
  519. }
  520. }
  521. public static string[] TableNames(SpreadsheetDocument sDoc)
  522. {
  523. var workbookXDoc = sDoc.WorkbookPart.GetXDocument();
  524. var sheets = workbookXDoc.Root.Elements(S.sheets).Elements(S.sheet);
  525. var tableNames = sheets.Select(sh =>
  526. {
  527. var rId = (string)sh.Attribute(R.id);
  528. var sheetPart = sDoc.WorkbookPart.GetPartById(rId);
  529. var sheetXDoc = sheetPart.GetXDocument();
  530. var tableParts = sheetXDoc.Root.Element(S.tableParts);
  531. if (tableParts == null)
  532. return new List<string>();
  533. var tableNames2 = tableParts
  534. .Elements(S.tablePart)
  535. .Select(tp =>
  536. {
  537. var tpRId = (string)tp.Attribute(R.id);
  538. var tpart = sheetPart.GetPartById(tpRId);
  539. var tpxd = tpart.GetXDocument();
  540. var name = (string)tpxd.Root.Attribute("name");
  541. return name;
  542. })
  543. .ToList();
  544. return tableNames2;
  545. })
  546. .SelectMany(m => m)
  547. .ToArray();
  548. return tableNames;
  549. }
  550. private static XElement GetTableProps(Table table)
  551. {
  552. XElement tableProps = new XElement("TableProps");
  553. XElement tableStyleInfo = table.TableStyleInfo;
  554. if (tableStyleInfo != null)
  555. {
  556. var newTableStyleInfo = TransformRemoveNamespace(tableStyleInfo);
  557. tableProps.Add(newTableStyleInfo);
  558. }
  559. if (!tableProps.HasElements && !tableProps.HasElements)
  560. tableProps = null;
  561. return tableProps;
  562. }
  563. private static XElement GetDataProps(XDocument shXDoc)
  564. {
  565. var sheetFormatPr = shXDoc.Root.Element(S.sheetFormatPr);
  566. if (sheetFormatPr != null && sheetFormatPr.Attribute("defaultColWidth") == null)
  567. sheetFormatPr.Add(new XAttribute("defaultColWidth", "9.25"));
  568. if (sheetFormatPr != null && sheetFormatPr.Attribute("defaultRowHeight") == null)
  569. sheetFormatPr.Add(new XAttribute("defaultRowHeight", "14.25"));
  570. var mergeCells = TransformRemoveNamespace(shXDoc.Root.Element(S.mergeCells));
  571. var dataProps = new XElement("DataProps",
  572. TransformRemoveNamespace(sheetFormatPr),
  573. TransformRemoveNamespace(shXDoc.Root.Element(S.cols)),
  574. mergeCells);
  575. if (!dataProps.HasAttributes && !dataProps.HasElements)
  576. dataProps = null;
  577. return dataProps;
  578. }
  579. private static XElement GetRowProps(XElement rowElement)
  580. {
  581. var rowProps = new XElement("RowProps");
  582. var ht = rowElement.Attribute("ht");
  583. if (ht != null)
  584. rowProps.Add(ht);
  585. var dyDescent = rowElement.Attribute(x14ac + "dyDescent");
  586. if (dyDescent != null)
  587. rowProps.Add(new XAttribute("dyDescent", (string)dyDescent));
  588. if (!rowProps.HasAttributes && !rowProps.HasElements)
  589. rowProps = null;
  590. return rowProps;
  591. }
  592. private static XElement GetCellProps_NotInTable(SpreadsheetDocument sDoc, XDocument styleXDoc, XElement cell)
  593. {
  594. var cellProps = new XElement("CellProps");
  595. var style = (int?)cell.Attribute("s");
  596. if (style == null)
  597. return cellProps;
  598. var xf = styleXDoc
  599. .Root
  600. .Elements(S.cellXfs)
  601. .Elements(S.xf)
  602. .Skip((int)style)
  603. .FirstOrDefault();
  604. var numFmtId = (int?)xf.Attribute("numFmtId");
  605. if (numFmtId != null)
  606. AddNumFmtIdAndFormatCode(styleXDoc, cellProps, numFmtId);
  607. var masterXfId = (int?)xf.Attribute("xfId");
  608. if (masterXfId != null)
  609. {
  610. var masterXf = styleXDoc
  611. .Root
  612. .Elements(S.cellStyleXfs)
  613. .Elements(S.xf)
  614. .Skip((int)masterXfId)
  615. .FirstOrDefault();
  616. if (masterXf != null)
  617. AddFormattingToCellProps(styleXDoc, cellProps, masterXf);
  618. }
  619. AddFormattingToCellProps(styleXDoc, cellProps, xf);
  620. AugmentAndCleanUpProps(cellProps);
  621. if (!cellProps.HasElements && !cellProps.HasAttributes)
  622. return null;
  623. return cellProps;
  624. }
  625. private static XElement GetCellProps_InTable(SpreadsheetDocument sDoc, XDocument styleXDoc, Table table, Cell tc)
  626. {
  627. var style = tc.Style;
  628. if (style == null)
  629. return null;
  630. var colIdStr = tc.ColumnAddress;
  631. int colNbr = XlsxTables.ColumnAddressToIndex(colIdStr);
  632. TableColumn column = table.TableColumns().FirstOrDefault(z => z.ColumnNumber == colNbr);
  633. if (column == null)
  634. throw new FileFormatException("Invalid spreadsheet");
  635. var cellProps = new XElement("CellProps");
  636. var d = column.DataDxfId;
  637. if (d != null)
  638. {
  639. var dataDxf = styleXDoc
  640. .Root
  641. .Elements(S.dxfs)
  642. .Elements(S.dxf)
  643. .Skip((int)d)
  644. .FirstOrDefault();
  645. if (dataDxf == null)
  646. throw new FileFormatException("Invalid spreadsheet");
  647. var numFmt = dataDxf.Element(S.numFmt);
  648. if (numFmt != null)
  649. {
  650. var numFmtId = (int?)numFmt.Attribute("numFmtId");
  651. if (numFmtId != null)
  652. cellProps.Add(new XAttribute("numFmtId", numFmtId));
  653. var formatCode = (string)numFmt.Attribute("formatCode");
  654. if (formatCode != null)
  655. cellProps.Add(new XAttribute("formatCode", formatCode));
  656. }
  657. }
  658. var xf = styleXDoc.Root
  659. .Elements(S.cellXfs)
  660. .Elements(S.xf)
  661. .Skip((int)style)
  662. .FirstOrDefault();
  663. if (xf == null)
  664. throw new FileFormatException("Invalid spreadsheet");
  665. // if xf has different numFmtId, then replace the ones from the table definition
  666. var numFmtId2 = (int?)xf.Attribute("numFmtId");
  667. if (numFmtId2 != null)
  668. AddNumFmtIdAndFormatCode(styleXDoc, cellProps, numFmtId2);
  669. var masterXfId = (int?)xf.Attribute("xfId");
  670. if (masterXfId != null)
  671. {
  672. var masterXf = styleXDoc
  673. .Root
  674. .Elements(S.cellStyleXfs)
  675. .Elements(S.xf)
  676. .Skip((int)masterXfId)
  677. .FirstOrDefault();
  678. if (masterXf != null)
  679. AddFormattingToCellProps(styleXDoc, cellProps, masterXf);
  680. }
  681. AddFormattingToCellProps(styleXDoc, cellProps, xf);
  682. AugmentAndCleanUpProps(cellProps);
  683. if (!cellProps.HasElements && !cellProps.HasAttributes)
  684. return null;
  685. return cellProps;
  686. }
  687. private static void AddNumFmtIdAndFormatCode(XDocument styleXDoc, XElement props, int? numFmtId)
  688. {
  689. var existingNumFmtId = props.Attribute("numFmtId");
  690. if (existingNumFmtId != null)
  691. existingNumFmtId.Value = numFmtId.ToString();
  692. else
  693. props.Add(new XAttribute("numFmtId", numFmtId));
  694. var numFmt = styleXDoc
  695. .Root
  696. .Elements(S.numFmts)
  697. .Elements(S.numFmt)
  698. .FirstOrDefault(z => (int)z.Attribute("numFmtId") == numFmtId);
  699. if (numFmt == null)
  700. {
  701. var formatCode = GetFormatCodeFromFmtId((int)numFmtId);
  702. if (formatCode != null)
  703. {
  704. var existingFormatCode = props.Attribute("formatCode");
  705. if (existingFormatCode != null)
  706. existingFormatCode.Value = formatCode;
  707. else
  708. props.Add(new XAttribute("formatCode", formatCode));
  709. }
  710. }
  711. else
  712. {
  713. var formatCode = (string)numFmt.Attribute("formatCode");
  714. if (formatCode != null)
  715. {
  716. var existingFormatCode = props.Attribute("formatCode");
  717. if (existingFormatCode != null)
  718. existingFormatCode.Value = formatCode;
  719. else
  720. props.Add(new XAttribute("formatCode", formatCode));
  721. }
  722. }
  723. }
  724. private static void AddFormattingToCellProps(XDocument styleXDoc, XElement props, XElement xf)
  725. {
  726. MoveBooleanAttribute(props, xf, "applyAlignment");
  727. MoveBooleanAttribute(props, xf, "applyBorder");
  728. MoveBooleanAttribute(props, xf, "applyFill");
  729. MoveBooleanAttribute(props, xf, "applyFont");
  730. MoveBooleanAttribute(props, xf, "applyNumberFormat");
  731. int? borderId = (int?)xf.Attribute("borderId");
  732. int? fillId = (int?)xf.Attribute("fillId");
  733. int? fontId = (int?)xf.Attribute("fontId");
  734. if (fontId != null)
  735. {
  736. var fontElement = styleXDoc
  737. .Root
  738. .Elements(S.fonts)
  739. .Elements(S.font)
  740. .Skip((int)fontId)
  741. .FirstOrDefault();
  742. if (fontElement != null)
  743. {
  744. var newFontElement = (XElement)TransformRemoveNamespace(fontElement);
  745. AddOrReplaceElement(props, newFontElement);
  746. }
  747. }
  748. if (fillId != null)
  749. {
  750. var fillElement = styleXDoc
  751. .Root
  752. .Elements(S.fills)
  753. .Elements(S.fill)
  754. .Skip((int)fillId)
  755. .FirstOrDefault();
  756. if (fillElement != null)
  757. {
  758. var newFillElement = (XElement)TransformRemoveNamespace(fillElement);
  759. AddOrReplaceElement(props, newFillElement);
  760. }
  761. }
  762. if (borderId != null)
  763. {
  764. var borderElement = styleXDoc
  765. .Root
  766. .Elements(S.borders)
  767. .Elements(S.border)
  768. .Skip((int)borderId)
  769. .FirstOrDefault();
  770. if (borderElement != null)
  771. {
  772. var newborderElement = (XElement)TransformRemoveNamespace(borderElement);
  773. AddOrReplaceElement(props, newborderElement);
  774. }
  775. }
  776. if (xf.Element(S.alignment) != null)
  777. {
  778. var newAlignmentElement = (XElement)TransformRemoveNamespace(xf.Element(S.alignment));
  779. AddOrReplaceElement(props, newAlignmentElement);
  780. }
  781. }
  782. private static void MoveBooleanAttribute(XElement props, XElement xf, XName attributeName)
  783. {
  784. bool attrValue = ConvertAttributeToBool(xf.Attribute(attributeName));
  785. if (attrValue)
  786. {
  787. if (props.Attribute(attributeName) == null)
  788. props.Add(new XAttribute(attributeName, attrValue ? "1" : "0"));
  789. else
  790. props.Attribute(attributeName).Value = attrValue ? "1" : "0";
  791. }
  792. }
  793. public static string[] IndexedColors = new string[] {
  794. "00000000",
  795. "00FFFFFF",
  796. "00FF0000",
  797. "0000FF00",
  798. "000000FF",
  799. "00FFFF00",
  800. "00FF00FF",
  801. "0000FFFF",
  802. "00000000",
  803. "00FFFFFF",
  804. "00FF0000",
  805. "0000FF00",
  806. "000000FF",
  807. "00FFFF00",
  808. "00FF00FF",
  809. "0000FFFF",
  810. "00800000",
  811. "00008000",
  812. "00000080",
  813. "00808000",
  814. "00800080",
  815. "00008080",
  816. "00C0C0C0",
  817. "00808080",
  818. "009999FF",
  819. "00993366",
  820. "00FFFFCC",
  821. "00CCFFFF",
  822. "00660066",
  823. "00FF8080",
  824. "000066CC",
  825. "00CCCCFF",
  826. "00000080",
  827. "00FF00FF",
  828. "00FFFF00",
  829. "0000FFFF",
  830. "00800080",
  831. "00800000",
  832. "00008080",
  833. "000000FF",
  834. "0000CCFF",
  835. "00CCFFFF",
  836. "00CCFFCC",
  837. "00FFFF99",
  838. "0099CCFF",
  839. "00FF99CC",
  840. "00CC99FF",
  841. "00FFCC99",
  842. "003366FF",
  843. "0033CCCC",
  844. "0099CC00",
  845. "00FFCC00",
  846. "00FF9900",
  847. "00FF6600",
  848. "00666699",
  849. "00969696",
  850. "00003366",
  851. "00339966",
  852. "00003300",
  853. "00333300",
  854. "00993300",
  855. "00993366",
  856. "00333399",
  857. "00333333",
  858. "System Foreground",
  859. "System Background",
  860. };
  861. private static string[] FontFamilyList = new string[] {
  862. "Not applicable",
  863. "Roman",
  864. "Swiss",
  865. "Modern",
  866. "Script",
  867. "Decorative",
  868. };
  869. private static void AugmentAndCleanUpProps(XElement props)
  870. {
  871. foreach (var color in props.Descendants("color").Where(c => c.Attribute("indexed") != null).ToList())
  872. {
  873. var idx = (int)color.Attribute("indexed");
  874. if (idx < IndexedColors.Length)
  875. {
  876. color.Add(new XAttribute("val", IndexedColors[idx]));
  877. }
  878. color.Attribute("indexed").Remove();
  879. }
  880. foreach (var family in props.Descendants("family").ToList())
  881. {
  882. var fam = (int?)family.Attribute("val");
  883. if (fam != null)
  884. {
  885. if (fam < FontFamilyList.Length)
  886. {
  887. family.Attribute("val").Remove();
  888. family.Add(new XAttribute("val", FontFamilyList[(int)fam]));
  889. }
  890. }
  891. }
  892. foreach (var border in props.Descendants("border").ToList())
  893. {
  894. RemoveIfEmpty(border.Element("left"));
  895. RemoveIfEmpty(border.Element("right"));
  896. RemoveIfEmpty(border.Element("top"));
  897. RemoveIfEmpty(border.Element("bottom"));
  898. RemoveIfEmpty(border.Element("diagonal"));
  899. if (!border.HasAttributes && !border.HasElements)
  900. border.Remove();
  901. }
  902. foreach (var fill in props.Descendants("fill").ToList())
  903. {
  904. fill.Elements("patternFill").Where(pf => (string)pf.Attribute("patternType") == "none").Remove();
  905. if (!fill.HasAttributes && !fill.HasElements)
  906. fill.Remove();
  907. }
  908. }
  909. private static void RemoveIfEmpty(XElement xElement)
  910. {
  911. if (xElement == null)
  912. return;
  913. if (!xElement.HasAttributes && !xElement.HasElements)
  914. xElement.Remove();
  915. }
  916. private static object TransformRemoveNamespace(XNode node)
  917. {
  918. if (node == null)
  919. return null;
  920. XElement element = node as XElement;
  921. if (element != null)
  922. {
  923. return new XElement(element.Name.LocalName,
  924. element.Attributes().Select(a => new XAttribute(a.Name.LocalName, (string)a)).OrderBy(a => (string)a.Name.LocalName),
  925. element.Nodes().Select(n => TransformRemoveNamespace(n)));
  926. }
  927. return node;
  928. }
  929. private static string GetFormatCodeFromFmtId(int fmtId)
  930. {
  931. switch (fmtId)
  932. {
  933. case 0: return "General";
  934. case 1: return "0";
  935. case 2: return "0.00";
  936. case 3: return "#,##0";
  937. case 4: return "#,##0.00";
  938. case 9: return "0%";
  939. case 10: return "0.00%";
  940. case 11: return "0.00E+00";
  941. case 12: return "# ?/?";
  942. case 13: return "# ??/??";
  943. case 14: return "mm-dd-yy";
  944. case 15: return "d-mmm-yy";
  945. case 16: return "d-mmm";
  946. case 17: return "mmm-yy";
  947. case 18: return "h:mm AM/PM";
  948. case 19: return "h:mm:ss AM/PM";
  949. case 20: return "h:mm";
  950. case 21: return "h:mm:ss";
  951. case 22: return "22 m/d/yy h:mm";
  952. case 37: return "#,##0 ;(#,##0)";
  953. case 38: return "#,##0 ;[Red](#,##0)";
  954. case 39: return "#,##0.00;(#,##0.00)";
  955. case 40: return "#,##0.00;[Red](#,##0.00)";
  956. case 45: return "mm:ss";
  957. case 46: return "[h]:mm:ss";
  958. case 47: return "mmss.0";
  959. case 48: return "##0.0E+0";
  960. case 49: return "@";
  961. default: return null;
  962. }
  963. }
  964. private static void AddOrReplaceElement(XElement props, XName childElementName, int value)
  965. {
  966. var existingElement = props.Element(childElementName);
  967. if (existingElement != null)
  968. existingElement.ReplaceWith(new XElement(childElementName, new XAttribute("Val", value)));
  969. else
  970. props.Add(new XElement(childElementName, new XAttribute("Val", value)));
  971. }
  972. private static void AddOrReplaceElement(XElement props, XName childElementName, string value)
  973. {
  974. var existingElement = props.Element(childElementName);
  975. if (existingElement != null)
  976. existingElement.ReplaceWith(new XElement(childElementName, new XAttribute("Val", value)));
  977. else
  978. props.Add(new XElement(childElementName, new XAttribute("Val", value)));
  979. }
  980. private static void AddOrReplaceElement(XElement props, XElement element)
  981. {
  982. var existingElement = props.Element(element.Name);
  983. if (existingElement != null)
  984. existingElement.ReplaceWith(element);
  985. else
  986. props.Add(element);
  987. }
  988. private static bool ConvertAttributeToBool(XAttribute xAttribute)
  989. {
  990. string applyNumberFormatStr = (string)xAttribute;
  991. bool returnValue = false;
  992. if (applyNumberFormatStr != null)
  993. {
  994. if (applyNumberFormatStr == "1")
  995. returnValue = true;
  996. if (applyNumberFormatStr.Substring(0, 1).ToUpper() == "T")
  997. returnValue = true;
  998. }
  999. return returnValue;
  1000. }
  1001. private static XNamespace x14ac = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac";
  1002. }
  1003. }