You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Container.cs 39KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Xml.Linq;
  5. using System.IO.Packaging;
  6. using System.Collections.Generic;
  7. using System.Text.RegularExpressions;
  8. using System.Collections.ObjectModel;
  9. namespace Novacode
  10. {
  11. public abstract class Container : DocXElement
  12. {
  13. public virtual ReadOnlyCollection<Content> Contents
  14. {
  15. get
  16. {
  17. List<Content> contents = GetContents();
  18. return contents.AsReadOnly();
  19. }
  20. }
  21. /// <summary>
  22. /// Returns a list of all Paragraphs inside this container.
  23. /// </summary>
  24. /// <example>
  25. /// <code>
  26. /// Load a document.
  27. /// using (DocX document = DocX.Load(@"Test.docx"))
  28. /// {
  29. /// // All Paragraphs in this document.
  30. /// <![CDATA[ List<Paragraph> ]]> documentParagraphs = document.Paragraphs;
  31. ///
  32. /// // Make sure this document contains at least one Table.
  33. /// if (document.Tables.Count() > 0)
  34. /// {
  35. /// // Get the first Table in this document.
  36. /// Table t = document.Tables[0];
  37. ///
  38. /// // All Paragraphs in this Table.
  39. /// <![CDATA[ List<Paragraph> ]]> tableParagraphs = t.Paragraphs;
  40. ///
  41. /// // Make sure this Table contains at least one Row.
  42. /// if (t.Rows.Count() > 0)
  43. /// {
  44. /// // Get the first Row in this document.
  45. /// Row r = t.Rows[0];
  46. ///
  47. /// // All Paragraphs in this Row.
  48. /// <![CDATA[ List<Paragraph> ]]> rowParagraphs = r.Paragraphs;
  49. ///
  50. /// // Make sure this Row contains at least one Cell.
  51. /// if (r.Cells.Count() > 0)
  52. /// {
  53. /// // Get the first Cell in this document.
  54. /// Cell c = r.Cells[0];
  55. ///
  56. /// // All Paragraphs in this Cell.
  57. /// <![CDATA[ List<Paragraph> ]]> cellParagraphs = c.Paragraphs;
  58. /// }
  59. /// }
  60. /// }
  61. ///
  62. /// // Save all changes to this document.
  63. /// document.Save();
  64. /// }// Release this document from memory.
  65. /// </code>
  66. /// </example>
  67. public virtual ReadOnlyCollection<Paragraph> Paragraphs
  68. {
  69. get
  70. {
  71. List<Paragraph> paragraphs = GetParagraphs();
  72. foreach (var p in paragraphs)
  73. {
  74. if ((p.Xml.ElementsAfterSelf().FirstOrDefault() != null) && (p.Xml.ElementsAfterSelf().First().Name.Equals(DocX.w + "tbl")))
  75. p.FollowingTable = new Table(this.Document, p.Xml.ElementsAfterSelf().First());
  76. p.ParentContainer = GetParentFromXmlName(p.Xml.Ancestors().First().Name.LocalName);
  77. if (p.IsListItem)
  78. {
  79. GetListItemType(p);
  80. }
  81. }
  82. return paragraphs.AsReadOnly();
  83. }
  84. }
  85. public virtual ReadOnlyCollection<Paragraph> ParagraphsDeepSearch
  86. {
  87. get
  88. {
  89. List<Paragraph> paragraphs = GetParagraphs(true);
  90. foreach (var p in paragraphs)
  91. {
  92. if ((p.Xml.ElementsAfterSelf().FirstOrDefault() != null) && (p.Xml.ElementsAfterSelf().First().Name.Equals(DocX.w + "tbl")))
  93. p.FollowingTable = new Table(this.Document, p.Xml.ElementsAfterSelf().First());
  94. p.ParentContainer = GetParentFromXmlName(p.Xml.Ancestors().First().Name.LocalName);
  95. if (p.IsListItem)
  96. {
  97. GetListItemType(p);
  98. }
  99. }
  100. return paragraphs.AsReadOnly();
  101. }
  102. }
  103. /// <summary>
  104. /// Removes paragraph at specified position
  105. /// </summary>
  106. /// <param name="index">Index of paragraph to remove</param>
  107. /// <returns>True if removed</returns>
  108. public bool RemoveParagraphAt(int index)
  109. {
  110. int i = 0;
  111. foreach (var paragraph in Xml.Descendants(DocX.w + "p"))
  112. {
  113. if (i == index)
  114. {
  115. paragraph.Remove();
  116. return true;
  117. }
  118. ++i;
  119. }
  120. return false;
  121. }
  122. /// <summary>
  123. /// Removes paragraph
  124. /// </summary>
  125. /// <param name="p">Paragraph to remove</param>
  126. /// <returns>True if removed</returns>
  127. public bool RemoveParagraph(Paragraph p)
  128. {
  129. foreach (var paragraph in Xml.Descendants(DocX.w + "p"))
  130. {
  131. if (paragraph.Equals(p.Xml))
  132. {
  133. paragraph.Remove();
  134. return true;
  135. }
  136. }
  137. return false;
  138. }
  139. public virtual List<Section> Sections
  140. {
  141. get
  142. {
  143. var allParas = Paragraphs;
  144. var parasInASection = new List<Paragraph>();
  145. var sections = new List<Section>();
  146. foreach (var para in allParas)
  147. {
  148. var sectionInPara = para.Xml.Descendants().FirstOrDefault(s => s.Name.LocalName == "sectPr");
  149. if (sectionInPara == null)
  150. {
  151. parasInASection.Add(para);
  152. }
  153. else
  154. {
  155. parasInASection.Add(para);
  156. var section = new Section(Document, sectionInPara) { SectionParagraphs = parasInASection };
  157. sections.Add(section);
  158. parasInASection = new List<Paragraph>();
  159. }
  160. }
  161. XElement body = Xml.Element(XName.Get("body", DocX.w.NamespaceName));
  162. if (body != null)
  163. {
  164. XElement baseSectionXml = body.Element(XName.Get("sectPr", DocX.w.NamespaceName));
  165. var baseSection = new Section(Document, baseSectionXml) { SectionParagraphs = parasInASection };
  166. sections.Add(baseSection);
  167. }
  168. return sections;
  169. }
  170. }
  171. private void GetListItemType(Paragraph p)
  172. {
  173. var ilvlNode = p.ParagraphNumberProperties.Descendants().FirstOrDefault(el => el.Name.LocalName == "ilvl");
  174. var ilvlValue = ilvlNode.Attribute(DocX.w + "val").Value;
  175. var numIdNode = p.ParagraphNumberProperties.Descendants().FirstOrDefault(el => el.Name.LocalName == "numId");
  176. var numIdValue = numIdNode.Attribute(DocX.w + "val").Value;
  177. //find num node in numbering
  178. var numNodes = Document.numbering.Descendants().Where(n => n.Name.LocalName == "num");
  179. XElement numNode = numNodes.FirstOrDefault(node => node.Attribute(DocX.w + "numId").Value.Equals(numIdValue));
  180. if (numNode != null)
  181. {
  182. //Get abstractNumId node and its value from numNode
  183. var abstractNumIdNode = numNode.Descendants().First(n => n.Name.LocalName == "abstractNumId");
  184. var abstractNumNodeValue = abstractNumIdNode.Attribute(DocX.w + "val").Value;
  185. var abstractNumNodes = Document.numbering.Descendants().Where(n => n.Name.LocalName == "abstractNum");
  186. XElement abstractNumNode =
  187. abstractNumNodes.FirstOrDefault(node => node.Attribute(DocX.w + "abstractNumId").Value.Equals(abstractNumNodeValue));
  188. //Find lvl node
  189. var lvlNodes = abstractNumNode.Descendants().Where(n => n.Name.LocalName == "lvl");
  190. XElement lvlNode = null;
  191. foreach (XElement node in lvlNodes)
  192. {
  193. if (node.Attribute(DocX.w + "ilvl").Value.Equals(ilvlValue))
  194. {
  195. lvlNode = node;
  196. break;
  197. }
  198. }
  199. var numFmtNode = lvlNode.Descendants().First(n => n.Name.LocalName == "numFmt");
  200. p.ListItemType = GetListItemType(numFmtNode.Attribute(DocX.w + "val").Value);
  201. }
  202. }
  203. public ContainerType ParentContainer;
  204. internal List<Content> GetContents(bool deepSearch = false)
  205. {
  206. // Need some memory that can be updated by the recursive search.
  207. //int index = 0;
  208. List<Content> contents = new List<Content>();
  209. foreach (XElement e in Xml.Descendants(XName.Get("sdt", DocX.w.NamespaceName)))
  210. {
  211. Content content = new Content(Document, e, 0);
  212. XElement el = e.Elements(XName.Get("sdtPr", DocX.w.NamespaceName)).First();
  213. content.Name = GetAttribute(el, "alias", "val");
  214. content.Tag = GetAttribute(el, "tag", "val");
  215. contents.Add(content);
  216. }
  217. return contents;
  218. }
  219. private string GetAttribute(XElement e, string localName, string attributeName)
  220. {
  221. string val = string.Empty;
  222. try
  223. {
  224. val = e.Elements(XName.Get(localName, DocX.w.NamespaceName)).Attributes(XName.Get(attributeName, DocX.w.NamespaceName)).FirstOrDefault().Value;
  225. }
  226. catch (Exception)
  227. {
  228. val = "Missing";
  229. }
  230. return val;
  231. }
  232. internal List<Paragraph> GetParagraphs(bool deepSearch = false)
  233. {
  234. // Need some memory that can be updated by the recursive search.
  235. int index = 0;
  236. List<Paragraph> paragraphs = new List<Paragraph>();
  237. foreach (XElement e in Xml.Descendants(XName.Get("p", DocX.w.NamespaceName)))
  238. {
  239. Paragraph paragraph = new Paragraph(Document, e, index);
  240. paragraphs.Add(paragraph);
  241. index += HelperFunctions.GetText(e).Length;
  242. }
  243. // GetParagraphsRecursive(Xml, ref index, ref paragraphs, deepSearch);
  244. return paragraphs;
  245. }
  246. internal void GetParagraphsRecursive(XElement Xml, ref int index, ref List<Paragraph> paragraphs, bool deepSearch = false)
  247. {
  248. // sdtContent are for PageNumbers inside Headers or Footers, don't go any deeper.
  249. //if (Xml.Name.LocalName == "sdtContent")
  250. // return;
  251. var keepSearching = true;
  252. if (Xml.Name.LocalName == "p")
  253. {
  254. paragraphs.Add(new Paragraph(Document, Xml, index));
  255. index += HelperFunctions.GetText(Xml).Length;
  256. if (!deepSearch)
  257. keepSearching = false;
  258. }
  259. if (keepSearching && Xml.HasElements)
  260. {
  261. foreach (XElement e in Xml.Elements())
  262. {
  263. GetParagraphsRecursive(e, ref index, ref paragraphs, deepSearch);
  264. }
  265. }
  266. }
  267. public virtual List<Table> Tables
  268. {
  269. get
  270. {
  271. List<Table> tables =
  272. (
  273. from t in Xml.Descendants(DocX.w + "tbl")
  274. select new Table(Document, t)
  275. ).ToList();
  276. return tables;
  277. }
  278. }
  279. public virtual List<List> Lists
  280. {
  281. get
  282. {
  283. var lists = new List<List>();
  284. var list = new List(Document, Xml);
  285. foreach (var paragraph in Paragraphs)
  286. {
  287. if (paragraph.IsListItem)
  288. {
  289. if (list.CanAddListItem(paragraph))
  290. {
  291. list.AddItem(paragraph);
  292. }
  293. else
  294. {
  295. lists.Add(list);
  296. list = new List(Document, Xml);
  297. list.AddItem(paragraph);
  298. }
  299. }
  300. }
  301. lists.Add(list);
  302. return lists;
  303. }
  304. }
  305. public virtual List<Hyperlink> Hyperlinks
  306. {
  307. get
  308. {
  309. List<Hyperlink> hyperlinks = new List<Hyperlink>();
  310. foreach (Paragraph p in Paragraphs)
  311. hyperlinks.AddRange(p.Hyperlinks);
  312. return hyperlinks;
  313. }
  314. }
  315. public virtual List<Picture> Pictures
  316. {
  317. get
  318. {
  319. List<Picture> pictures = new List<Picture>();
  320. foreach (Paragraph p in Paragraphs)
  321. pictures.AddRange(p.Pictures);
  322. return pictures;
  323. }
  324. }
  325. /// <summary>
  326. /// Sets the Direction of content.
  327. /// </summary>
  328. /// <param name="direction">Direction either LeftToRight or RightToLeft</param>
  329. /// <example>
  330. /// Set the Direction of content in a Paragraph to RightToLeft.
  331. /// <code>
  332. /// // Load a document.
  333. /// using (DocX document = DocX.Load(@"Test.docx"))
  334. /// {
  335. /// // Get the first Paragraph from this document.
  336. /// Paragraph p = document.InsertParagraph();
  337. ///
  338. /// // Set the Direction of this Paragraph.
  339. /// p.Direction = Direction.RightToLeft;
  340. ///
  341. /// // Make sure the document contains at lest one Table.
  342. /// if (document.Tables.Count() > 0)
  343. /// {
  344. /// // Get the first Table from this document.
  345. /// Table t = document.Tables[0];
  346. ///
  347. /// /*
  348. /// * Set the direction of the entire Table.
  349. /// * Note: The same function is available at the Row and Cell level.
  350. /// */
  351. /// t.SetDirection(Direction.RightToLeft);
  352. /// }
  353. ///
  354. /// // Save all changes to this document.
  355. /// document.Save();
  356. /// }// Release this document from memory.
  357. /// </code>
  358. /// </example>
  359. public virtual void SetDirection(Direction direction)
  360. {
  361. foreach (Paragraph p in Paragraphs)
  362. p.Direction = direction;
  363. }
  364. public virtual List<int> FindAll(string str)
  365. {
  366. return FindAll(str, RegexOptions.None);
  367. }
  368. public virtual List<int> FindAll(string str, RegexOptions options)
  369. {
  370. List<int> list = new List<int>();
  371. foreach (Paragraph p in Paragraphs)
  372. {
  373. List<int> indexes = p.FindAll(str, options);
  374. for (int i = 0; i < indexes.Count(); i++)
  375. indexes[i] += p.startIndex;
  376. list.AddRange(indexes);
  377. }
  378. return list;
  379. }
  380. /// <summary>
  381. /// Find all unique instances of the given Regex Pattern,
  382. /// returning the list of the unique strings found
  383. /// </summary>
  384. /// <param name="pattern"></param>
  385. /// <param name="options"></param>
  386. /// <returns></returns>
  387. public virtual List<string> FindUniqueByPattern(string pattern, RegexOptions options)
  388. {
  389. List<string> rawResults = new List<string>();
  390. foreach (Paragraph p in Paragraphs)
  391. { // accumulate the search results from all paragraphs
  392. List<string> partials = p.FindAllByPattern(pattern, options);
  393. rawResults.AddRange(partials);
  394. }
  395. // this dictionary is used to collect results and test for uniqueness
  396. Dictionary<string, int> uniqueResults = new Dictionary<string, int>();
  397. foreach (string currValue in rawResults)
  398. {
  399. if (!uniqueResults.ContainsKey(currValue))
  400. { // if the dictionary doesn't have it, add it
  401. uniqueResults.Add(currValue, 0);
  402. }
  403. }
  404. return uniqueResults.Keys.ToList(); // return the unique list of results
  405. }
  406. public virtual void ReplaceText(string searchValue, string newValue, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch, bool escapeRegEx = true, bool useRegExSubstitutions = false, bool removeEmptyParagraph = true)
  407. {
  408. if (string.IsNullOrEmpty(searchValue))
  409. throw new ArgumentException("oldValue cannot be null or empty", "searchValue");
  410. if (newValue == null)
  411. throw new ArgumentException("newValue cannot be null or empty", "newValue");
  412. // ReplaceText in Headers of the document.
  413. var headerList = new List<Header> { Document.Headers.first, Document.Headers.even, Document.Headers.odd };
  414. foreach (var header in headerList)
  415. if (header != null)
  416. foreach (var paragraph in header.Paragraphs)
  417. paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
  418. // ReplaceText int main body of document.
  419. foreach (var paragraph in Paragraphs)
  420. paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
  421. // ReplaceText in Footers of the document.
  422. var footerList = new List<Footer> { Document.Footers.first, Document.Footers.even, Document.Footers.odd };
  423. foreach (var footer in footerList)
  424. if (footer != null)
  425. foreach (var paragraph in footer.Paragraphs)
  426. paragraph.ReplaceText(searchValue, newValue, trackChanges, options, newFormatting, matchFormatting, formattingOptions, escapeRegEx, useRegExSubstitutions, removeEmptyParagraph);
  427. }
  428. /// <summary>
  429. ///
  430. /// </summary>
  431. /// <param name="searchValue">Value to find</param>
  432. /// <param name="regexMatchHandler">A Func that accepts the matching regex search group value and passes it to this to return the replacement string</param>
  433. /// <param name="trackChanges">Enable trackchanges</param>
  434. /// <param name="options">Regex options</param>
  435. /// <param name="newFormatting"></param>
  436. /// <param name="matchFormatting"></param>
  437. /// <param name="formattingOptions"></param>
  438. /// <param name="removeEmptyParagraph">Remove empty paragraph</param>
  439. public virtual void ReplaceText(string searchValue, Func<string, string> regexMatchHandler, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions formattingOptions = MatchFormattingOptions.SubsetMatch, bool removeEmptyParagraph = true)
  440. {
  441. if (string.IsNullOrEmpty(searchValue))
  442. throw new ArgumentException("oldValue cannot be null or empty", "searchValue");
  443. if (regexMatchHandler == null)
  444. throw new ArgumentException("regexMatchHandler cannot be null", "regexMatchHandler");
  445. // ReplaceText in Headers/Footers of the document.
  446. var containerList = new List<IParagraphContainer> {
  447. Document.Headers.first, Document.Headers.even, Document.Headers.odd,
  448. Document.Footers.first, Document.Footers.even, Document.Footers.odd };
  449. foreach (var container in containerList)
  450. if (container != null)
  451. foreach (var paragraph in container.Paragraphs)
  452. paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions, removeEmptyParagraph);
  453. // ReplaceText int main body of document.
  454. foreach (var paragraph in Paragraphs)
  455. paragraph.ReplaceText(searchValue, regexMatchHandler, trackChanges, options, newFormatting, matchFormatting, formattingOptions, removeEmptyParagraph);
  456. }
  457. /// <summary>
  458. /// Removes all items with required formatting
  459. /// </summary>
  460. /// <returns>Numer of texts removed</returns>
  461. public int RemoveTextInGivenFormat(Formatting matchFormatting, MatchFormattingOptions fo = MatchFormattingOptions.SubsetMatch)
  462. {
  463. var deletedCount = 0;
  464. foreach (var x in Xml.Elements())
  465. {
  466. deletedCount += RemoveTextWithFormatRecursive(x, matchFormatting, fo);
  467. }
  468. return deletedCount;
  469. }
  470. internal int RemoveTextWithFormatRecursive(XElement element, Formatting matchFormatting, MatchFormattingOptions fo)
  471. {
  472. var deletedCount = 0;
  473. foreach (var x in element.Elements())
  474. {
  475. if ("rPr".Equals(x.Name.LocalName))
  476. {
  477. if (HelperFunctions.ContainsEveryChildOf(matchFormatting.Xml, x, fo))
  478. {
  479. x.Parent.Remove();
  480. ++deletedCount;
  481. }
  482. }
  483. deletedCount += RemoveTextWithFormatRecursive(x, matchFormatting, fo);
  484. }
  485. return deletedCount;
  486. }
  487. public virtual void InsertAtBookmark(string toInsert, string bookmarkName)
  488. {
  489. if (bookmarkName.IsNullOrWhiteSpace())
  490. throw new ArgumentException("bookmark cannot be null or empty", "bookmarkName");
  491. var headerCollection = Document.Headers;
  492. var headers = new List<Header> { headerCollection.first, headerCollection.even, headerCollection.odd };
  493. foreach (var header in headers.Where(x => x != null))
  494. foreach (var paragraph in header.Paragraphs)
  495. paragraph.InsertAtBookmark(toInsert, bookmarkName);
  496. foreach (var paragraph in Paragraphs)
  497. paragraph.InsertAtBookmark(toInsert, bookmarkName);
  498. var footerCollection = Document.Footers;
  499. var footers = new List<Footer> { footerCollection.first, footerCollection.even, footerCollection.odd };
  500. foreach (var footer in footers.Where(x => x != null))
  501. foreach (var paragraph in footer.Paragraphs)
  502. paragraph.InsertAtBookmark(toInsert, bookmarkName);
  503. }
  504. public string[] ValidateBookmarks(params string[] bookmarkNames)
  505. {
  506. var headers = new[] { Document.Headers.first, Document.Headers.even, Document.Headers.odd }.Where(h => h != null).ToList();
  507. var footers = new[] { Document.Footers.first, Document.Footers.even, Document.Footers.odd }.Where(f => f != null).ToList();
  508. var nonMatching = new List<string>();
  509. foreach (var bookmarkName in bookmarkNames)
  510. {
  511. if (headers.SelectMany(h => h.Paragraphs).Any(p => p.ValidateBookmark(bookmarkName))) return new string[0];
  512. if (footers.SelectMany(h => h.Paragraphs).Any(p => p.ValidateBookmark(bookmarkName))) return new string[0];
  513. if (Paragraphs.Any(p => p.ValidateBookmark(bookmarkName))) return new string[0];
  514. nonMatching.Add(bookmarkName);
  515. }
  516. return nonMatching.ToArray();
  517. }
  518. public virtual Paragraph InsertParagraph(int index, string text, bool trackChanges)
  519. {
  520. return InsertParagraph(index, text, trackChanges, null);
  521. }
  522. public virtual Paragraph InsertParagraph()
  523. {
  524. return InsertParagraph(string.Empty, false);
  525. }
  526. public virtual Paragraph InsertParagraph(int index, Paragraph p)
  527. {
  528. XElement newXElement = new XElement(p.Xml);
  529. p.Xml = newXElement;
  530. Paragraph paragraph = HelperFunctions.GetFirstParagraphEffectedByInsert(Document, index);
  531. if (paragraph == null)
  532. Xml.Add(p.Xml);
  533. else
  534. {
  535. XElement[] split = HelperFunctions.SplitParagraph(paragraph, index - paragraph.startIndex);
  536. paragraph.Xml.ReplaceWith
  537. (
  538. split[0],
  539. newXElement,
  540. split[1]
  541. );
  542. }
  543. GetParent(p);
  544. return p;
  545. }
  546. public virtual Paragraph InsertParagraph(Paragraph p)
  547. {
  548. #region Styles
  549. XDocument style_document;
  550. if (p.styles.Count() > 0)
  551. {
  552. Uri style_package_uri = new Uri("/word/styles.xml", UriKind.Relative);
  553. if (!Document.package.PartExists(style_package_uri))
  554. {
  555. PackagePart style_package = Document.package.CreatePart(style_package_uri, "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", CompressionOption.Maximum);
  556. using (TextWriter tw = new StreamWriter(new PackagePartStream(style_package.GetStream())))
  557. {
  558. style_document = new XDocument
  559. (
  560. new XDeclaration("1.0", "UTF-8", "yes"),
  561. new XElement(XName.Get("styles", DocX.w.NamespaceName))
  562. );
  563. style_document.Save(tw);
  564. }
  565. }
  566. PackagePart styles_document = Document.package.GetPart(style_package_uri);
  567. using (TextReader tr = new StreamReader(styles_document.GetStream()))
  568. {
  569. style_document = XDocument.Load(tr);
  570. XElement styles_element = style_document.Element(XName.Get("styles", DocX.w.NamespaceName));
  571. var ids = from d in styles_element.Descendants(XName.Get("style", DocX.w.NamespaceName))
  572. let a = d.Attribute(XName.Get("styleId", DocX.w.NamespaceName))
  573. where a != null
  574. select a.Value;
  575. foreach (XElement style in p.styles)
  576. {
  577. // If styles_element does not contain this element, then add it.
  578. if (!ids.Contains(style.Attribute(XName.Get("styleId", DocX.w.NamespaceName)).Value))
  579. styles_element.Add(style);
  580. }
  581. }
  582. using (TextWriter tw = new StreamWriter(new PackagePartStream(styles_document.GetStream())))
  583. style_document.Save(tw);
  584. }
  585. #endregion
  586. XElement newXElement = new XElement(p.Xml);
  587. Xml.Add(newXElement);
  588. int index = 0;
  589. if (Document.paragraphLookup.Keys.Count() > 0)
  590. {
  591. index = Document.paragraphLookup.Last().Key;
  592. if (Document.paragraphLookup.Last().Value.Text.Length == 0)
  593. index++;
  594. else
  595. index += Document.paragraphLookup.Last().Value.Text.Length;
  596. }
  597. Paragraph newParagraph = new Paragraph(Document, newXElement, index);
  598. Document.paragraphLookup.Add(index, newParagraph);
  599. GetParent(newParagraph);
  600. return newParagraph;
  601. }
  602. public virtual Paragraph InsertParagraph(int index, string text, bool trackChanges, Formatting formatting)
  603. {
  604. Paragraph newParagraph = new Paragraph(Document, new XElement(DocX.w + "p"), index);
  605. newParagraph.InsertText(0, text, trackChanges, formatting);
  606. Paragraph firstPar = HelperFunctions.GetFirstParagraphEffectedByInsert(Document, index);
  607. if (firstPar != null)
  608. {
  609. var splitindex = index - firstPar.startIndex;
  610. if (splitindex <= 0)
  611. {
  612. firstPar.Xml.ReplaceWith(newParagraph.Xml, firstPar.Xml);
  613. }
  614. else
  615. {
  616. XElement[] splitParagraph = HelperFunctions.SplitParagraph(firstPar, splitindex);
  617. firstPar.Xml.ReplaceWith
  618. (
  619. splitParagraph[0],
  620. newParagraph.Xml,
  621. splitParagraph[1]
  622. );
  623. }
  624. }
  625. else
  626. Xml.Add(newParagraph);
  627. GetParent(newParagraph);
  628. return newParagraph;
  629. }
  630. private ContainerType GetParentFromXmlName(string xmlName)
  631. {
  632. ContainerType parent;
  633. switch (xmlName)
  634. {
  635. case "body":
  636. parent = ContainerType.Body;
  637. break;
  638. case "p":
  639. parent = ContainerType.Paragraph;
  640. break;
  641. case "tbl":
  642. parent = ContainerType.Table;
  643. break;
  644. case "sectPr":
  645. parent = ContainerType.Section;
  646. break;
  647. case "tc":
  648. parent = ContainerType.Cell;
  649. break;
  650. default:
  651. parent = ContainerType.None;
  652. break;
  653. }
  654. return parent;
  655. }
  656. private void GetParent(Paragraph newParagraph)
  657. {
  658. var containerType = GetType();
  659. switch (containerType.Name)
  660. {
  661. case "Body":
  662. newParagraph.ParentContainer = ContainerType.Body;
  663. break;
  664. case "Table":
  665. newParagraph.ParentContainer = ContainerType.Table;
  666. break;
  667. case "TOC":
  668. newParagraph.ParentContainer = ContainerType.TOC;
  669. break;
  670. case "Section":
  671. newParagraph.ParentContainer = ContainerType.Section;
  672. break;
  673. case "Cell":
  674. newParagraph.ParentContainer = ContainerType.Cell;
  675. break;
  676. case "Header":
  677. newParagraph.ParentContainer = ContainerType.Header;
  678. break;
  679. case "Footer":
  680. newParagraph.ParentContainer = ContainerType.Footer;
  681. break;
  682. case "Paragraph":
  683. newParagraph.ParentContainer = ContainerType.Paragraph;
  684. break;
  685. }
  686. }
  687. private ListItemType GetListItemType(string styleName)
  688. {
  689. ListItemType listItemType;
  690. switch (styleName)
  691. {
  692. case "bullet":
  693. listItemType = ListItemType.Bulleted;
  694. break;
  695. default:
  696. listItemType = ListItemType.Numbered;
  697. break;
  698. }
  699. return listItemType;
  700. }
  701. public virtual void InsertSection()
  702. {
  703. InsertSection(false);
  704. }
  705. public virtual void InsertSection(bool trackChanges)
  706. {
  707. var newParagraphSection = new XElement
  708. (
  709. XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName), new XElement(XName.Get("sectPr", DocX.w.NamespaceName), new XElement(XName.Get("type", DocX.w.NamespaceName), new XAttribute(DocX.w + "val", "continuous"))))
  710. );
  711. if (trackChanges)
  712. newParagraphSection = HelperFunctions.CreateEdit(EditType.ins, DateTime.Now, newParagraphSection);
  713. Xml.Add(newParagraphSection);
  714. }
  715. public virtual void InsertSectionPageBreak(bool trackChanges = false)
  716. {
  717. var newParagraphSection = new XElement
  718. (
  719. XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName), new XElement(XName.Get("sectPr", DocX.w.NamespaceName)))
  720. );
  721. if (trackChanges)
  722. newParagraphSection = HelperFunctions.CreateEdit(EditType.ins, DateTime.Now, newParagraphSection);
  723. Xml.Add(newParagraphSection);
  724. }
  725. public virtual Paragraph InsertParagraph(string text)
  726. {
  727. return InsertParagraph(text, false, new Formatting());
  728. }
  729. public virtual Paragraph InsertParagraph(string text, bool trackChanges)
  730. {
  731. return InsertParagraph(text, trackChanges, new Formatting());
  732. }
  733. public virtual Paragraph InsertParagraph(string text, bool trackChanges, Formatting formatting)
  734. {
  735. XElement newParagraph = new XElement
  736. (
  737. XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName)), HelperFunctions.FormatInput(text, formatting.Xml)
  738. );
  739. if (trackChanges)
  740. newParagraph = HelperFunctions.CreateEdit(EditType.ins, DateTime.Now, newParagraph);
  741. Xml.Add(newParagraph);
  742. var paragraphAdded = new Paragraph(Document, newParagraph, 0);
  743. if (this is Cell)
  744. {
  745. var cell = this as Cell;
  746. paragraphAdded.PackagePart = cell.mainPart;
  747. }
  748. else if (this is DocX)
  749. {
  750. paragraphAdded.PackagePart = Document.mainPart;
  751. }
  752. else if (this is Footer)
  753. {
  754. var f = this as Footer;
  755. paragraphAdded.mainPart = f.mainPart;
  756. }
  757. else if (this is Header)
  758. {
  759. var h = this as Header;
  760. paragraphAdded.mainPart = h.mainPart;
  761. }
  762. else
  763. {
  764. Console.WriteLine("No idea what we are {0}", this);
  765. paragraphAdded.PackagePart = Document.mainPart;
  766. }
  767. GetParent(paragraphAdded);
  768. return paragraphAdded;
  769. }
  770. public virtual Paragraph InsertEquation(string equation)
  771. {
  772. Paragraph p = InsertParagraph();
  773. p.AppendEquation(equation);
  774. return p;
  775. }
  776. public virtual Paragraph InsertBookmark(String bookmarkName)
  777. {
  778. var p = InsertParagraph();
  779. p.AppendBookmark(bookmarkName);
  780. return p;
  781. }
  782. public virtual Table InsertTable(int rowCount, int columnCount) //Dmitchern, changed to virtual, and overrided in Table.Cell
  783. {
  784. XElement newTable = HelperFunctions.CreateTable(rowCount, columnCount);
  785. Xml.Add(newTable);
  786. return new Table(Document, newTable) { mainPart = mainPart };
  787. }
  788. public Table InsertTable(int index, int rowCount, int columnCount)
  789. {
  790. XElement newTable = HelperFunctions.CreateTable(rowCount, columnCount);
  791. Paragraph p = HelperFunctions.GetFirstParagraphEffectedByInsert(Document, index);
  792. if (p == null)
  793. Xml.Elements().First().AddFirst(newTable);
  794. else
  795. {
  796. XElement[] split = HelperFunctions.SplitParagraph(p, index - p.startIndex);
  797. p.Xml.ReplaceWith
  798. (
  799. split[0],
  800. newTable,
  801. split[1]
  802. );
  803. }
  804. return new Table(Document, newTable) { mainPart = mainPart };
  805. }
  806. public Table InsertTable(Table t)
  807. {
  808. XElement newXElement = new XElement(t.Xml);
  809. Xml.Add(newXElement);
  810. Table newTable = new Table(Document, newXElement)
  811. {
  812. mainPart = mainPart,
  813. Design = t.Design
  814. };
  815. return newTable;
  816. }
  817. public Table InsertTable(int index, Table t)
  818. {
  819. Paragraph p = HelperFunctions.GetFirstParagraphEffectedByInsert(Document, index);
  820. XElement[] split = HelperFunctions.SplitParagraph(p, index - p.startIndex);
  821. XElement newXElement = new XElement(t.Xml);
  822. p.Xml.ReplaceWith
  823. (
  824. split[0],
  825. newXElement,
  826. split[1]
  827. );
  828. Table newTable = new Table(Document, newXElement)
  829. {
  830. mainPart = mainPart,
  831. Design = t.Design
  832. };
  833. return newTable;
  834. }
  835. internal Container(DocX document, XElement xml)
  836. : base(document, xml)
  837. {
  838. }
  839. public List InsertList(List list)
  840. {
  841. foreach (var item in list.Items)
  842. {
  843. Xml.Add(item.Xml);
  844. }
  845. return list;
  846. }
  847. public List InsertList(List list, double fontSize)
  848. {
  849. foreach (var item in list.Items)
  850. {
  851. item.FontSize(fontSize);
  852. Xml.Add(item.Xml);
  853. }
  854. return list;
  855. }
  856. public List InsertList(List list, Font fontFamily, double fontSize)
  857. {
  858. foreach (var item in list.Items)
  859. {
  860. item.Font(fontFamily);
  861. item.FontSize(fontSize);
  862. Xml.Add(item.Xml);
  863. }
  864. return list;
  865. }
  866. public List InsertList(int index, List list)
  867. {
  868. Paragraph p = HelperFunctions.GetFirstParagraphEffectedByInsert(Document, index);
  869. XElement[] split = HelperFunctions.SplitParagraph(p, index - p.startIndex);
  870. var elements = new List<XElement> { split[0] };
  871. elements.AddRange(list.Items.Select(i => new XElement(i.Xml)));
  872. elements.Add(split[1]);
  873. p.Xml.ReplaceWith(elements.ToArray());
  874. return list;
  875. }
  876. }
  877. }