Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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