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.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Xml.Linq;
  6. using System.Text.RegularExpressions;
  7. using System.Security.Principal;
  8. using System.Collections;
  9. using System.IO.Packaging;
  10. using System.IO;
  11. using System.Drawing;
  12. namespace Novacode
  13. {
  14. /// <summary>
  15. /// Represents a document paragraph.
  16. /// </summary>
  17. public class Paragraph
  18. {
  19. internal List<XElement> runs;
  20. // This paragraphs text alignment
  21. private Alignment alignment;
  22. // A lookup for the runs in this paragraph
  23. Dictionary<int, Run> runLookup = new Dictionary<int, Run>();
  24. // The underlying XElement which this Paragraph wraps
  25. internal XElement xml;
  26. internal int startIndex, endIndex;
  27. // A collection of Images in this Paragraph
  28. private List<Picture> pictures;
  29. /// <summary>
  30. /// Returns a list of Pictures in this Paragraph.
  31. /// </summary>
  32. public List<Picture> Pictures { get { return pictures; } }
  33. // A collection of field type DocProperty.
  34. private List<DocProperty> docProperties;
  35. internal List<XElement> styles = new List<XElement>();
  36. /// <summary>
  37. /// Returns a list of field type DocProperty in this document.
  38. /// </summary>
  39. public List<DocProperty> DocumentProperties
  40. {
  41. get { return docProperties; }
  42. }
  43. internal DocX document;
  44. internal Paragraph(DocX document, int startIndex, XElement p)
  45. {
  46. this.document = document;
  47. this.startIndex = startIndex;
  48. this.endIndex = startIndex + GetElementTextLength(p);
  49. this.xml = p;
  50. BuildRunLookup(p);
  51. // Get all of the images in this document
  52. pictures = (from i in p.Descendants(XName.Get("drawing", DocX.w.NamespaceName))
  53. select new Picture(i)).ToList();
  54. RebuildDocProperties();
  55. #region It's possible that a Paragraph may have pStyle references
  56. // Check if this Paragraph references any pStyle elements.
  57. var stylesElements = xml.Descendants(XName.Get("pStyle", DocX.w.NamespaceName));
  58. // If one or more pStyles are referenced.
  59. if (stylesElements.Count() > 0)
  60. {
  61. Uri style_package_uri = new Uri("/word/styles.xml", UriKind.Relative);
  62. PackagePart styles_document = document.package.GetPart(style_package_uri);
  63. using (TextReader tr = new StreamReader(styles_document.GetStream()))
  64. {
  65. XDocument style_document = XDocument.Load(tr);
  66. XElement styles_element = style_document.Element(XName.Get("styles", DocX.w.NamespaceName));
  67. var styles_element_ids = stylesElements.Select(e => e.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value);
  68. foreach(string id in styles_element_ids)
  69. {
  70. var style =
  71. (
  72. from d in styles_element.Descendants()
  73. let styleId = d.Attribute(XName.Get("styleId", DocX.w.NamespaceName))
  74. let type = d.Attribute(XName.Get("type", DocX.w.NamespaceName))
  75. where type != null && type.Value == "paragraph" && styleId != null && styleId.Value == id
  76. select d
  77. ).First();
  78. styles.Add(style);
  79. }
  80. }
  81. }
  82. #endregion
  83. #region Pictures
  84. // Check if this Paragraph contains any Pictures
  85. List<string> pictureElementIDs =
  86. (
  87. from d in xml.Descendants()
  88. let embed = d.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"))
  89. where embed != null
  90. select embed.Value
  91. ).ToList();
  92. #endregion
  93. }
  94. /// <summary>
  95. /// Insert a new Table before this Paragraph, this Table can be from this document or another document.
  96. /// </summary>
  97. /// <param name="t">The Table t to be inserted.</param>
  98. /// <returns>A new Table inserted before this Paragraph.</returns>
  99. /// <example>
  100. /// Insert a new Table before this Paragraph.
  101. /// <code>
  102. /// // Place holder for a Table.
  103. /// Table t;
  104. ///
  105. /// // Load document a.
  106. /// using (DocX documentA = DocX.Load(@"a.docx"))
  107. /// {
  108. /// // Get the first Table from this document.
  109. /// t = documentA.Tables[0];
  110. /// }
  111. ///
  112. /// // Load document b.
  113. /// using (DocX documentB = DocX.Load(@"b.docx"))
  114. /// {
  115. /// // Get the first Paragraph in document b.
  116. /// Paragraph p2 = documentB.Paragraphs[0];
  117. ///
  118. /// // Insert the Table from document a before this Paragraph.
  119. /// Table newTable = p2.InsertTableBeforeSelf(t);
  120. ///
  121. /// // Save all changes made to document b.
  122. /// documentB.Save();
  123. /// }// Release this document from memory.
  124. /// </code>
  125. /// </example>
  126. public Table InsertTableBeforeSelf(Table t)
  127. {
  128. xml.AddBeforeSelf(t.xml);
  129. XElement newlyInserted = xml.ElementsBeforeSelf().First();
  130. t.xml = newlyInserted;
  131. DocX.RebuildTables(document);
  132. DocX.RebuildParagraphs(document);
  133. return t;
  134. }
  135. /// <summary>
  136. /// Insert a new Table into this document before this Paragraph.
  137. /// </summary>
  138. /// <param name="rowCount">The number of rows this Table should have.</param>
  139. /// <param name="coloumnCount">The number of coloumns this Table should have.</param>
  140. /// <returns>A new Table inserted before this Paragraph.</returns>
  141. /// <example>
  142. /// <code>
  143. /// // Create a new document.
  144. /// using (DocX document = DocX.Create(@"Test.docx"))
  145. /// {
  146. /// //Insert a Paragraph into this document.
  147. /// Paragraph p = document.InsertParagraph("Hello World", false);
  148. ///
  149. /// // Insert a new Table before this Paragraph.
  150. /// Table newTable = p.InsertTableBeforeSelf(2, 2);
  151. /// newTable.Design = TableDesign.LightShadingAccent2;
  152. /// newTable.Alignment = Alignment.center;
  153. ///
  154. /// // Save all changes made to this document.
  155. /// document.Save();
  156. /// }// Release this document from memory.
  157. /// </code>
  158. /// </example>
  159. public Table InsertTableBeforeSelf(int rowCount, int coloumnCount)
  160. {
  161. XElement newTable = DocX.CreateTable(rowCount, coloumnCount);
  162. xml.AddBeforeSelf(newTable);
  163. XElement newlyInserted = xml.ElementsBeforeSelf().First();
  164. DocX.RebuildTables(document);
  165. DocX.RebuildParagraphs(document);
  166. return new Table(document, newlyInserted);
  167. }
  168. /// <summary>
  169. /// Insert a new Table after this Paragraph.
  170. /// </summary>
  171. /// <param name="t">The Table t to be inserted.</param>
  172. /// <returns>A new Table inserted after this Paragraph.</returns>
  173. /// <example>
  174. /// Insert a new Table after this Paragraph.
  175. /// <code>
  176. /// // Place holder for a Table.
  177. /// Table t;
  178. ///
  179. /// // Load document a.
  180. /// using (DocX documentA = DocX.Load(@"a.docx"))
  181. /// {
  182. /// // Get the first Table from this document.
  183. /// t = documentA.Tables[0];
  184. /// }
  185. ///
  186. /// // Load document b.
  187. /// using (DocX documentB = DocX.Load(@"b.docx"))
  188. /// {
  189. /// // Get the first Paragraph in document b.
  190. /// Paragraph p2 = documentB.Paragraphs[0];
  191. ///
  192. /// // Insert the Table from document a after this Paragraph.
  193. /// Table newTable = p2.InsertTableAfterSelf(t);
  194. ///
  195. /// // Save all changes made to document b.
  196. /// documentB.Save();
  197. /// }// Release this document from memory.
  198. /// </code>
  199. /// </example>
  200. public Table InsertTableAfterSelf(Table t)
  201. {
  202. xml.AddAfterSelf(t.xml);
  203. XElement newlyInserted = xml.ElementsAfterSelf().First();
  204. t.xml = newlyInserted;
  205. DocX.RebuildTables(document);
  206. DocX.RebuildParagraphs(document);
  207. return t;
  208. }
  209. /// <summary>
  210. /// Insert a new Table into this document after this Paragraph.
  211. /// </summary>
  212. /// <param name="rowCount">The number of rows this Table should have.</param>
  213. /// <param name="coloumnCount">The number of coloumns this Table should have.</param>
  214. /// <returns>A new Table inserted after this Paragraph.</returns>
  215. /// <example>
  216. /// <code>
  217. /// // Create a new document.
  218. /// using (DocX document = DocX.Create(@"Test.docx"))
  219. /// {
  220. /// //Insert a Paragraph into this document.
  221. /// Paragraph p = document.InsertParagraph("Hello World", false);
  222. ///
  223. /// // Insert a new Table after this Paragraph.
  224. /// Table newTable = p.InsertTableAfterSelf(2, 2);
  225. /// newTable.Design = TableDesign.LightShadingAccent2;
  226. /// newTable.Alignment = Alignment.center;
  227. ///
  228. /// // Save all changes made to this document.
  229. /// document.Save();
  230. /// }// Release this document from memory.
  231. /// </code>
  232. /// </example>
  233. public Table InsertTableAfterSelf(int rowCount, int coloumnCount)
  234. {
  235. XElement newTable = DocX.CreateTable(rowCount, coloumnCount);
  236. xml.AddAfterSelf(newTable);
  237. XElement newlyInserted = xml.ElementsAfterSelf().First();
  238. DocX.RebuildTables(document);
  239. DocX.RebuildParagraphs(document);
  240. return new Table(document, newlyInserted);
  241. }
  242. /// <summary>
  243. /// Insert a Paragraph before this Paragraph, this Paragraph may have come from the same or another document.
  244. /// </summary>
  245. /// <param name="p">The Paragraph to insert.</param>
  246. /// <returns>The Paragraph now associated with this document.</returns>
  247. /// <example>
  248. /// Take a Paragraph from document a, and insert it into document b before this Paragraph.
  249. /// <code>
  250. /// // Place holder for a Paragraph.
  251. /// Paragraph p;
  252. ///
  253. /// // Load document a.
  254. /// using (DocX documentA = DocX.Load(@"a.docx"))
  255. /// {
  256. /// // Get the first paragraph from this document.
  257. /// p = documentA.Paragraphs[0];
  258. /// }
  259. ///
  260. /// // Load document b.
  261. /// using (DocX documentB = DocX.Load(@"b.docx"))
  262. /// {
  263. /// // Get the first Paragraph in document b.
  264. /// Paragraph p2 = documentB.Paragraphs[0];
  265. ///
  266. /// // Insert the Paragraph from document a before this Paragraph.
  267. /// Paragraph newParagraph = p2.InsertParagraphBeforeSelf(p);
  268. ///
  269. /// // Save all changes made to document b.
  270. /// documentB.Save();
  271. /// }// Release this document from memory.
  272. /// </code>
  273. /// </example>
  274. public Paragraph InsertParagraphBeforeSelf(Paragraph p)
  275. {
  276. xml.AddBeforeSelf(p.xml);
  277. XElement newlyInserted = xml.ElementsBeforeSelf().First();
  278. p.xml = newlyInserted;
  279. DocX.RebuildParagraphs(document);
  280. return p;
  281. }
  282. /// <summary>
  283. /// Insert a new Paragraph before this Paragraph.
  284. /// </summary>
  285. /// <param name="text">The initial text for this new Paragraph.</param>
  286. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  287. /// <example>
  288. /// Insert a new paragraph before the first Paragraph in this document.
  289. /// <code>
  290. /// // Create a new document.
  291. /// using (DocX document = DocX.Create(@"Test.docx"))
  292. /// {
  293. /// // Insert a Paragraph into this document.
  294. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  295. ///
  296. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.");
  297. ///
  298. /// // Save all changes made to this new document.
  299. /// document.Save();
  300. /// }// Release this new document form memory.
  301. /// </code>
  302. /// </example>
  303. public Paragraph InsertParagraphBeforeSelf(string text)
  304. {
  305. return InsertParagraphBeforeSelf(text, false, new Formatting());
  306. }
  307. /// <summary>
  308. /// Insert a new Paragraph before this Paragraph.
  309. /// </summary>
  310. /// <param name="text">The initial text for this new Paragraph.</param>
  311. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  312. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  313. /// <example>
  314. /// Insert a new paragraph before the first Paragraph in this document.
  315. /// <code>
  316. /// // Create a new document.
  317. /// using (DocX document = DocX.Create(@"Test.docx"))
  318. /// {
  319. /// // Insert a Paragraph into this document.
  320. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  321. ///
  322. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false);
  323. ///
  324. /// // Save all changes made to this new document.
  325. /// document.Save();
  326. /// }// Release this new document form memory.
  327. /// </code>
  328. /// </example>
  329. public Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges)
  330. {
  331. return InsertParagraphBeforeSelf(text, trackChanges, new Formatting());
  332. }
  333. /// <summary>
  334. /// Insert a new Paragraph before this Paragraph.
  335. /// </summary>
  336. /// <param name="text">The initial text for this new Paragraph.</param>
  337. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  338. /// <param name="formatting">The formatting to apply to this insertion.</param>
  339. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  340. /// <example>
  341. /// Insert a new paragraph before the first Paragraph in this document.
  342. /// <code>
  343. /// // Create a new document.
  344. /// using (DocX document = DocX.Create(@"Test.docx"))
  345. /// {
  346. /// // Insert a Paragraph into this document.
  347. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  348. ///
  349. /// Formatting boldFormatting = new Formatting();
  350. /// boldFormatting.Bold = true;
  351. ///
  352. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false, boldFormatting);
  353. ///
  354. /// // Save all changes made to this new document.
  355. /// document.Save();
  356. /// }// Release this new document form memory.
  357. /// </code>
  358. /// </example>
  359. public Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges, Formatting formatting)
  360. {
  361. XElement newParagraph = new XElement
  362. (
  363. XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName)), DocX.FormatInput(text, formatting.Xml)
  364. );
  365. if (trackChanges)
  366. newParagraph = CreateEdit(EditType.ins, DateTime.Now, newParagraph);
  367. xml.AddBeforeSelf(newParagraph);
  368. XElement newlyInserted = xml.ElementsBeforeSelf().First();
  369. Paragraph p = new Paragraph(document, -1, newlyInserted);
  370. DocX.RebuildParagraphs(document);
  371. return p;
  372. }
  373. /// <summary>
  374. /// Insert a page break after a Paragraph.
  375. /// </summary>
  376. /// <example>
  377. /// Insert 2 Paragraphs into a document with a page break between them.
  378. /// <code>
  379. /// using (DocX document = DocX.Create(@"Test.docx"))
  380. /// {
  381. /// // Insert a new Paragraph.
  382. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  383. ///
  384. /// // Insert a page break after this Paragraph.
  385. /// p1.InsertPageBreakAfterSelf();
  386. ///
  387. /// // Insert a new Paragraph.
  388. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  389. ///
  390. /// // Save this document.
  391. /// document.Save();
  392. /// }// Release this document from memory.
  393. /// </code>
  394. /// </example>
  395. public void InsertPageBreakAfterSelf()
  396. {
  397. XElement p = new XElement
  398. (
  399. XName.Get("p", DocX.w.NamespaceName),
  400. new XElement
  401. (
  402. XName.Get("r", DocX.w.NamespaceName),
  403. new XElement
  404. (
  405. XName.Get("br", DocX.w.NamespaceName),
  406. new XAttribute(XName.Get("type", DocX.w.NamespaceName), "page")
  407. )
  408. )
  409. );
  410. xml.AddAfterSelf(p);
  411. }
  412. /// <summary>
  413. /// Insert a page break before a Paragraph.
  414. /// </summary>
  415. /// <example>
  416. /// Insert 2 Paragraphs into a document with a page break between them.
  417. /// <code>
  418. /// using (DocX document = DocX.Create(@"Test.docx"))
  419. /// {
  420. /// // Insert a new Paragraph.
  421. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  422. ///
  423. /// // Insert a new Paragraph.
  424. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  425. ///
  426. /// // Insert a page break before Paragraph two.
  427. /// p2.InsertPageBreakBeforeSelf();
  428. ///
  429. /// // Save this document.
  430. /// document.Save();
  431. /// }// Release this document from memory.
  432. /// </code>
  433. /// </example>
  434. public void InsertPageBreakBeforeSelf()
  435. {
  436. XElement p = new XElement
  437. (
  438. XName.Get("p", DocX.w.NamespaceName),
  439. new XElement
  440. (
  441. XName.Get("r", DocX.w.NamespaceName),
  442. new XElement
  443. (
  444. XName.Get("br", DocX.w.NamespaceName),
  445. new XAttribute(XName.Get("type", DocX.w.NamespaceName), "page")
  446. )
  447. )
  448. );
  449. xml.AddBeforeSelf(p);
  450. }
  451. /// <summary>
  452. /// Insert a Paragraph after this Paragraph, this Paragraph may have come from the same or another document.
  453. /// </summary>
  454. /// <param name="p">The Paragraph to insert.</param>
  455. /// <returns>The Paragraph now associated with this document.</returns>
  456. /// <example>
  457. /// Take a Paragraph from document a, and insert it into document b after this Paragraph.
  458. /// <code>
  459. /// // Place holder for a Paragraph.
  460. /// Paragraph p;
  461. ///
  462. /// // Load document a.
  463. /// using (DocX documentA = DocX.Load(@"a.docx"))
  464. /// {
  465. /// // Get the first paragraph from this document.
  466. /// p = documentA.Paragraphs[0];
  467. /// }
  468. ///
  469. /// // Load document b.
  470. /// using (DocX documentB = DocX.Load(@"b.docx"))
  471. /// {
  472. /// // Get the first Paragraph in document b.
  473. /// Paragraph p2 = documentB.Paragraphs[0];
  474. ///
  475. /// // Insert the Paragraph from document a after this Paragraph.
  476. /// Paragraph newParagraph = p2.InsertParagraphAfterSelf(p);
  477. ///
  478. /// // Save all changes made to document b.
  479. /// documentB.Save();
  480. /// }// Release this document from memory.
  481. /// </code>
  482. /// </example>
  483. public Paragraph InsertParagraphAfterSelf(Paragraph p)
  484. {
  485. xml.AddAfterSelf(p.xml);
  486. XElement newlyInserted = xml.ElementsAfterSelf().First();
  487. p.xml = newlyInserted;
  488. DocX.RebuildParagraphs(document);
  489. return p;
  490. }
  491. /// <summary>
  492. /// Insert a new Paragraph after this Paragraph.
  493. /// </summary>
  494. /// <param name="text">The initial text for this new Paragraph.</param>
  495. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  496. /// <param name="formatting">The formatting to apply to this insertion.</param>
  497. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  498. /// <example>
  499. /// Insert a new paragraph after the first Paragraph in this document.
  500. /// <code>
  501. /// // Create a new document.
  502. /// using (DocX document = DocX.Create(@"Test.docx"))
  503. /// {
  504. /// // Insert a Paragraph into this document.
  505. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  506. ///
  507. /// Formatting boldFormatting = new Formatting();
  508. /// boldFormatting.Bold = true;
  509. ///
  510. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false, boldFormatting);
  511. ///
  512. /// // Save all changes made to this new document.
  513. /// document.Save();
  514. /// }// Release this new document form memory.
  515. /// </code>
  516. /// </example>
  517. public Paragraph InsertParagraphAfterSelf(string text, bool trackChanges, Formatting formatting)
  518. {
  519. XElement newParagraph = new XElement
  520. (
  521. XName.Get("p", DocX.w.NamespaceName), new XElement(XName.Get("pPr", DocX.w.NamespaceName)), DocX.FormatInput(text, formatting.Xml)
  522. );
  523. if (trackChanges)
  524. newParagraph = CreateEdit(EditType.ins, DateTime.Now, newParagraph);
  525. xml.AddAfterSelf(newParagraph);
  526. XElement newlyInserted = xml.ElementsAfterSelf().First();
  527. Paragraph p = new Paragraph(document, -1, newlyInserted);
  528. DocX.RebuildParagraphs(document);
  529. return p;
  530. }
  531. /// <summary>
  532. /// Insert a new Paragraph after this Paragraph.
  533. /// </summary>
  534. /// <param name="text">The initial text for this new Paragraph.</param>
  535. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  536. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  537. /// <example>
  538. /// Insert a new paragraph after the first Paragraph in this document.
  539. /// <code>
  540. /// // Create a new document.
  541. /// using (DocX document = DocX.Create(@"Test.docx"))
  542. /// {
  543. /// // Insert a Paragraph into this document.
  544. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  545. ///
  546. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false);
  547. ///
  548. /// // Save all changes made to this new document.
  549. /// document.Save();
  550. /// }// Release this new document form memory.
  551. /// </code>
  552. /// </example>
  553. public Paragraph InsertParagraphAfterSelf(string text, bool trackChanges)
  554. {
  555. return InsertParagraphAfterSelf(text, trackChanges, new Formatting());
  556. }
  557. /// <summary>
  558. /// Insert a new Paragraph after this Paragraph.
  559. /// </summary>
  560. /// <param name="text">The initial text for this new Paragraph.</param>
  561. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  562. /// <example>
  563. /// Insert a new paragraph after the first Paragraph in this document.
  564. /// <code>
  565. /// // Create a new document.
  566. /// using (DocX document = DocX.Create(@"Test.docx"))
  567. /// {
  568. /// // Insert a Paragraph into this document.
  569. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  570. ///
  571. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.");
  572. ///
  573. /// // Save all changes made to this new document.
  574. /// document.Save();
  575. /// }// Release this new document form memory.
  576. /// </code>
  577. /// </example>
  578. public Paragraph InsertParagraphAfterSelf(string text)
  579. {
  580. return InsertParagraphAfterSelf(text, false, new Formatting());
  581. }
  582. private void RebuildDocProperties()
  583. {
  584. docProperties =
  585. (
  586. from dp in xml.Descendants(XName.Get("fldSimple", DocX.w.NamespaceName))
  587. select new DocProperty(dp)
  588. ).ToList();
  589. }
  590. /// <summary>
  591. /// Gets or set this Paragraphs text alignment.
  592. /// </summary>
  593. public Alignment Alignment
  594. {
  595. get { return alignment; }
  596. set
  597. {
  598. alignment = value;
  599. XElement pPr = xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  600. if (alignment != Novacode.Alignment.left)
  601. {
  602. if (pPr == null)
  603. xml.Add(new XElement(XName.Get("pPr", DocX.w.NamespaceName)));
  604. pPr = xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  605. XElement jc = pPr.Element(XName.Get("jc", DocX.w.NamespaceName));
  606. if(jc == null)
  607. pPr.Add(new XElement(XName.Get("jc", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), alignment.ToString())));
  608. else
  609. jc.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value = alignment.ToString();
  610. }
  611. else
  612. {
  613. if (pPr != null)
  614. {
  615. XElement jc = pPr.Element(XName.Get("jc", DocX.w.NamespaceName));
  616. if (jc != null)
  617. jc.Remove();
  618. }
  619. }
  620. }
  621. }
  622. /// <summary>
  623. /// Remove this Paragraph from the document.
  624. /// </summary>
  625. /// <param name="trackChanges">Should this remove be tracked as a change?</param>
  626. /// <example>
  627. /// Remove a Paragraph from a document and track it as a change.
  628. /// <code>
  629. /// // Create a document using a relative filename.
  630. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  631. /// {
  632. /// // Create and Insert a new Paragraph into this document.
  633. /// Paragraph p = document.InsertParagraph("Hello", false);
  634. ///
  635. /// // Remove the Paragraph and track this as a change.
  636. /// p.Remove(true);
  637. ///
  638. /// // Save all changes made to this document.
  639. /// document.Save();
  640. /// }// Release this document from memory.
  641. /// </code>
  642. /// </example>
  643. public void Remove(bool trackChanges)
  644. {
  645. if (trackChanges)
  646. {
  647. DateTime now = DateTime.Now.ToUniversalTime();
  648. List<XElement> elements = xml.Elements().ToList();
  649. List<XElement> temp = new List<XElement>();
  650. for (int i = 0; i < elements.Count(); i++ )
  651. {
  652. XElement e = elements[i];
  653. if (e.Name.LocalName != "del")
  654. {
  655. temp.Add(e);
  656. e.Remove();
  657. }
  658. else
  659. {
  660. if (temp.Count() > 0)
  661. {
  662. e.AddBeforeSelf(CreateEdit(EditType.del, now, temp.Elements()));
  663. temp.Clear();
  664. }
  665. }
  666. }
  667. if (temp.Count() > 0)
  668. xml.Add(CreateEdit(EditType.del, now, temp));
  669. }
  670. else
  671. {
  672. runLookup.Clear();
  673. if (xml.Parent.Name.LocalName == "tc")
  674. xml.Value = string.Empty;
  675. else
  676. {
  677. // Remove this paragraph from the document
  678. xml.Remove();
  679. xml = null;
  680. runLookup = null;
  681. }
  682. }
  683. DocX.RebuildParagraphs(document);
  684. }
  685. internal void BuildRunLookup(XElement p)
  686. {
  687. runLookup.Clear();
  688. // Get the runs in this paragraph
  689. IEnumerable<XElement> runs = p.Descendants(XName.Get("r", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
  690. int startIndex = 0;
  691. // Loop through each run in this paragraph
  692. foreach (XElement run in runs)
  693. {
  694. // Only add runs which contain text
  695. if (GetElementTextLength(run) > 0)
  696. {
  697. Run r = new Run(startIndex, run);
  698. runLookup.Add(r.EndIndex, r);
  699. startIndex = r.EndIndex;
  700. }
  701. }
  702. }
  703. /// <summary>
  704. /// Gets the text value of this Paragraph.
  705. /// </summary>
  706. public string Text
  707. {
  708. // Returns the underlying XElement's Value property.
  709. get
  710. {
  711. StringBuilder sb = new StringBuilder();
  712. // Loop through each run in this paragraph
  713. foreach (XElement r in xml.Descendants(XName.Get("r", DocX.w.NamespaceName)))
  714. {
  715. // Loop through each text item in this run
  716. foreach (XElement descendant in r.Descendants())
  717. {
  718. switch (descendant.Name.LocalName)
  719. {
  720. case "tab":
  721. sb.Append("\t");
  722. break;
  723. case "br":
  724. sb.Append("\n");
  725. break;
  726. case "t":
  727. goto case "delText";
  728. case "delText":
  729. sb.Append(descendant.Value);
  730. break;
  731. default: break;
  732. }
  733. }
  734. }
  735. return sb.ToString();
  736. }
  737. }
  738. //public Picture InsertPicture(Picture picture)
  739. //{
  740. // Picture newPicture = picture;
  741. // newPicture.i = new XElement(picture.i);
  742. // xml.Add(newPicture.i);
  743. // pictures.Add(newPicture);
  744. // return newPicture;
  745. //}
  746. /// <summary>
  747. /// Insert a Picture at the end of this paragraph.
  748. /// </summary>
  749. /// <param name="description">A string to describe this Picture.</param>
  750. /// <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  751. /// <param name="name">The name of this image.</param>
  752. /// <returns>A Picture.</returns>
  753. /// <example>
  754. /// <code>
  755. /// // Create a document using a relative filename.
  756. /// using (DocX document = DocX.Create(@"Test.docx"))
  757. /// {
  758. /// // Add a new Paragraph to this document.
  759. /// Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  760. ///
  761. /// // Add an Image to this document.
  762. /// Novacode.Image img = document.AddImage(@"Image.jpg");
  763. ///
  764. /// // Insert pic at the end of Paragraph p.
  765. /// Picture pic = p.InsertPicture(img.Id, "Photo 31415", "A pie I baked.");
  766. ///
  767. /// // Rotate the Picture clockwise by 30 degrees.
  768. /// pic.Rotation = 30;
  769. ///
  770. /// // Resize the Picture.
  771. /// pic.Width = 400;
  772. /// pic.Height = 300;
  773. ///
  774. /// // Set the shape of this Picture to be a cube.
  775. /// pic.SetPictureShape(BasicShapes.cube);
  776. ///
  777. /// // Flip the Picture Horizontally.
  778. /// pic.FlipHorizontal = true;
  779. ///
  780. /// // Save all changes made to this document.
  781. /// document.Save();
  782. /// }// Release this document from memory.
  783. /// </code>
  784. /// </example>
  785. public Picture InsertPicture(string imageID, string name, string description)
  786. {
  787. Picture p = new Picture(document, imageID, name, description);
  788. xml.Add(p.xml);
  789. pictures.Add(p);
  790. return p;
  791. }
  792. public Picture InsertPicture(string imageID)
  793. {
  794. return InsertPicture(imageID, string.Empty, string.Empty);
  795. }
  796. //public Picture InsertPicture(int index, Picture picture)
  797. //{
  798. // Picture p = picture;
  799. // p.i = new XElement(picture.i);
  800. // Run run = GetFirstRunEffectedByEdit(index);
  801. // if (run == null)
  802. // xml.Add(p.i);
  803. // else
  804. // {
  805. // // Split this run at the point you want to insert
  806. // XElement[] splitRun = Run.SplitRun(run, index);
  807. // // Replace the origional run
  808. // run.xml.ReplaceWith
  809. // (
  810. // splitRun[0],
  811. // p.i,
  812. // splitRun[1]
  813. // );
  814. // }
  815. // // Rebuild the run lookup for this paragraph
  816. // runLookup.Clear();
  817. // BuildRunLookup(xml);
  818. // DocX.RenumberIDs(document);
  819. // return p;
  820. //}
  821. /// <summary>
  822. /// Insert a Picture into this Paragraph at a specified index.
  823. /// </summary>
  824. /// <param name="description">A string to describe this Picture.</param>
  825. /// <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  826. /// <param name="name">The name of this image.</param>
  827. /// <param name="index">The index to insert this Picture at.</param>
  828. /// <returns>A Picture.</returns>
  829. /// <example>
  830. /// <code>
  831. /// // Create a document using a relative filename.
  832. /// using (DocX document = DocX.Create(@"Test.docx"))
  833. /// {
  834. /// // Add a new Paragraph to this document.
  835. /// Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  836. ///
  837. /// // Add an Image to this document.
  838. /// Novacode.Image img = document.AddImage(@"Image.jpg");
  839. ///
  840. /// // Insert pic at the start of Paragraph p.
  841. /// Picture pic = p.InsertPicture(0, img.Id, "Photo 31415", "A pie I baked.");
  842. ///
  843. /// // Rotate the Picture clockwise by 30 degrees.
  844. /// pic.Rotation = 30;
  845. ///
  846. /// // Resize the Picture.
  847. /// pic.Width = 400;
  848. /// pic.Height = 300;
  849. ///
  850. /// // Set the shape of this Picture to be a cube.
  851. /// pic.SetPictureShape(BasicShapes.cube);
  852. ///
  853. /// // Flip the Picture Horizontally.
  854. /// pic.FlipHorizontal = true;
  855. ///
  856. /// // Save all changes made to this document.
  857. /// document.Save();
  858. /// }// Release this document from memory.
  859. /// </code>
  860. /// </example>
  861. public Picture InsertPicture(int index, string imageID, string name, string description)
  862. {
  863. Picture picture = new Picture(document, imageID, name, description);
  864. Run run = GetFirstRunEffectedByEdit(index);
  865. if (run == null)
  866. xml.Add(picture.xml);
  867. else
  868. {
  869. // Split this run at the point you want to insert
  870. XElement[] splitRun = Run.SplitRun(run, index);
  871. // Replace the origional run
  872. run.xml.ReplaceWith
  873. (
  874. splitRun[0],
  875. picture.xml,
  876. splitRun[1]
  877. );
  878. }
  879. // Rebuild the run lookup for this paragraph
  880. runLookup.Clear();
  881. BuildRunLookup(xml);
  882. DocX.RenumberIDs(document);
  883. return picture;
  884. }
  885. public Picture InsertPicture(int index, string imageID)
  886. {
  887. return InsertPicture(index, imageID, string.Empty, string.Empty);
  888. }
  889. /// <summary>
  890. /// Creates an Edit either a ins or a del with the specified content and date
  891. /// </summary>
  892. /// <param name="t">The type of this edit (ins or del)</param>
  893. /// <param name="edit_time">The time stamp to use for this edit</param>
  894. /// <param name="content">The initial content of this edit</param>
  895. /// <returns></returns>
  896. internal static XElement CreateEdit(EditType t, DateTime edit_time, object content)
  897. {
  898. if (t == EditType.del)
  899. {
  900. foreach (object o in (IEnumerable<XElement>)content)
  901. {
  902. if (o is XElement)
  903. {
  904. XElement e = (o as XElement);
  905. IEnumerable<XElement> ts = e.DescendantsAndSelf(XName.Get("t", DocX.w.NamespaceName));
  906. for(int i = 0; i < ts.Count(); i ++)
  907. {
  908. XElement text = ts.ElementAt(i);
  909. text.ReplaceWith(new XElement(DocX.w + "delText", text.Attributes(), text.Value));
  910. }
  911. }
  912. }
  913. }
  914. return
  915. (
  916. new XElement(DocX.w + t.ToString(),
  917. new XAttribute(DocX.w + "id", 0),
  918. new XAttribute(DocX.w + "author", WindowsIdentity.GetCurrent().Name),
  919. new XAttribute(DocX.w + "date", edit_time),
  920. content)
  921. );
  922. }
  923. internal Run GetFirstRunEffectedByEdit(int index)
  924. {
  925. foreach (int runEndIndex in runLookup.Keys)
  926. {
  927. if (runEndIndex > index)
  928. return runLookup[runEndIndex];
  929. }
  930. if (runLookup.Last().Value.EndIndex == index)
  931. return runLookup.Last().Value;
  932. throw new ArgumentOutOfRangeException();
  933. }
  934. internal Run GetFirstRunEffectedByInsert(int index)
  935. {
  936. // This paragraph contains no Runs and insertion is at index 0
  937. if (runLookup.Keys.Count() == 0 && index == 0)
  938. return null;
  939. foreach (int runEndIndex in runLookup.Keys)
  940. {
  941. if (runEndIndex >= index)
  942. return runLookup[runEndIndex];
  943. }
  944. throw new ArgumentOutOfRangeException();
  945. }
  946. /// <!--
  947. /// Bug found and fixed by krugs525 on August 12 2009.
  948. /// Use TFS compare to see exact code change.
  949. /// -->
  950. static internal int GetElementTextLength(XElement run)
  951. {
  952. int count = 0;
  953. if (run == null)
  954. return count;
  955. foreach (var d in run.Descendants())
  956. {
  957. switch (d.Name.LocalName)
  958. {
  959. case "tab":
  960. if (d.Parent.Name.LocalName != "tabs")
  961. goto case "br"; break;
  962. case "br": count++; break;
  963. case "t": goto case "delText";
  964. case "delText": count += d.Value.Length; break;
  965. default: break;
  966. }
  967. }
  968. return count;
  969. }
  970. internal XElement[] SplitEdit(XElement edit, int index, EditType type)
  971. {
  972. Run run;
  973. if(type == EditType.del)
  974. run = GetFirstRunEffectedByEdit(index);
  975. else
  976. run = GetFirstRunEffectedByInsert(index);
  977. XElement[] splitRun = Run.SplitRun(run, index);
  978. XElement splitLeft = new XElement(edit.Name, edit.Attributes(), run.xml.ElementsBeforeSelf(), splitRun[0]);
  979. if (GetElementTextLength(splitLeft) == 0)
  980. splitLeft = null;
  981. XElement splitRight = new XElement(edit.Name, edit.Attributes(), splitRun[1], run.xml.ElementsAfterSelf());
  982. if (GetElementTextLength(splitRight) == 0)
  983. splitRight = null;
  984. return
  985. (
  986. new XElement[]
  987. {
  988. splitLeft,
  989. splitRight
  990. }
  991. );
  992. }
  993. /// <summary>
  994. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  995. /// </summary>
  996. /// <example>
  997. /// <code>
  998. /// // Create a document using a relative filename.
  999. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1000. /// {
  1001. /// // Iterate through the Paragraphs in this document.
  1002. /// foreach (Paragraph p in document.Paragraphs)
  1003. /// {
  1004. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1005. /// p.InsertText(0, "Start: ", true);
  1006. /// }
  1007. ///
  1008. /// // Save all changes made to this document.
  1009. /// document.Save();
  1010. /// }// Release this document from memory.
  1011. /// </code>
  1012. /// </example>
  1013. /// <example>
  1014. /// Inserting tabs using the \t switch.
  1015. /// <code>
  1016. /// // Create a document using a relative filename.
  1017. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1018. /// {
  1019. /// // Iterate through the paragraphs in this document.
  1020. /// foreach (Paragraph p in document.Paragraphs)
  1021. /// {
  1022. /// // Insert the string "\tStart:\t" at the begining of every paragraph and flag it as a change.
  1023. /// p.InsertText(0, "\tStart:\t", true);
  1024. /// }
  1025. ///
  1026. /// // Save all changes made to this document.
  1027. /// document.Save();
  1028. /// }// Release this document from memory.
  1029. /// </code>
  1030. /// </example>
  1031. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1032. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1033. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1034. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1035. /// <param name="index">The index position of the insertion.</param>
  1036. /// <param name="value">The System.String to insert.</param>
  1037. /// <param name="trackChanges">Flag this insert as a change.</param>
  1038. public void InsertText(int index, string value, bool trackChanges)
  1039. {
  1040. InsertText(index, value, trackChanges, null);
  1041. }
  1042. /// <summary>
  1043. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1044. /// </summary>
  1045. /// <example>
  1046. /// <code>
  1047. /// // Create a document using a relative filename.
  1048. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1049. /// {
  1050. /// // Iterate through the Paragraphs in this document.
  1051. /// foreach (Paragraph p in document.Paragraphs)
  1052. /// {
  1053. /// // Insert the string "End: " at the end of every Paragraph and flag it as a change.
  1054. /// p.InsertText("End: ", true);
  1055. /// }
  1056. ///
  1057. /// // Save all changes made to this document.
  1058. /// document.Save();
  1059. /// }// Release this document from memory.
  1060. /// </code>
  1061. /// </example>
  1062. /// <example>
  1063. /// Inserting tabs using the \t switch.
  1064. /// <code>
  1065. /// // Create a document using a relative filename.
  1066. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1067. /// {
  1068. /// // Iterate through the paragraphs in this document.
  1069. /// foreach (Paragraph p in document.Paragraphs)
  1070. /// {
  1071. /// // Insert the string "\tEnd" at the end of every paragraph and flag it as a change.
  1072. /// p.InsertText("\tEnd", true);
  1073. /// }
  1074. ///
  1075. /// // Save all changes made to this document.
  1076. /// document.Save();
  1077. /// }// Release this document from memory.
  1078. /// </code>
  1079. /// </example>
  1080. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1081. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1082. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1083. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1084. /// <param name="value">The System.String to insert.</param>
  1085. /// <param name="trackChanges">Flag this insert as a change.</param>
  1086. public void InsertText(string value, bool trackChanges)
  1087. {
  1088. List<XElement> newRuns = DocX.FormatInput(value, null);
  1089. xml.Add(newRuns);
  1090. runLookup.Clear();
  1091. BuildRunLookup(xml);
  1092. DocX.RenumberIDs(document);
  1093. }
  1094. /// <summary>
  1095. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1096. /// </summary>
  1097. /// <example>
  1098. /// <code>
  1099. /// // Create a document using a relative filename.
  1100. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1101. /// {
  1102. /// // Create a text formatting.
  1103. /// Formatting f = new Formatting();
  1104. /// f.FontColor = Color.Red;
  1105. /// f.Size = 30;
  1106. ///
  1107. /// // Iterate through the Paragraphs in this document.
  1108. /// foreach (Paragraph p in document.Paragraphs)
  1109. /// {
  1110. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1111. /// p.InsertText("Start: ", true, f);
  1112. /// }
  1113. ///
  1114. /// // Save all changes made to this document.
  1115. /// document.Save();
  1116. /// }// Release this document from memory.
  1117. /// </code>
  1118. /// </example>
  1119. /// <example>
  1120. /// Inserting tabs using the \t switch.
  1121. /// <code>
  1122. /// // Create a document using a relative filename.
  1123. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1124. /// {
  1125. /// // Create a text formatting.
  1126. /// Formatting f = new Formatting();
  1127. /// f.FontColor = Color.Red;
  1128. /// f.Size = 30;
  1129. ///
  1130. /// // Iterate through the paragraphs in this document.
  1131. /// foreach (Paragraph p in document.Paragraphs)
  1132. /// {
  1133. /// // Insert the string "\tEnd" at the end of every paragraph and flag it as a change.
  1134. /// p.InsertText("\tEnd", true, f);
  1135. /// }
  1136. ///
  1137. /// // Save all changes made to this document.
  1138. /// document.Save();
  1139. /// }// Release this document from memory.
  1140. /// </code>
  1141. /// </example>
  1142. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1143. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1144. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1145. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1146. /// <param name="value">The System.String to insert.</param>
  1147. /// <param name="trackChanges">Flag this insert as a change.</param>
  1148. /// <param name="formatting">The text formatting.</param>
  1149. public void InsertText(string value, bool trackChanges, Formatting formatting)
  1150. {
  1151. List<XElement> newRuns = DocX.FormatInput(value, formatting.Xml);
  1152. xml.Add(newRuns);
  1153. runLookup.Clear();
  1154. BuildRunLookup(xml);
  1155. DocX.RenumberIDs(document);
  1156. DocX.RebuildParagraphs(document);
  1157. }
  1158. /// <summary>
  1159. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1160. /// </summary>
  1161. /// <example>
  1162. /// <code>
  1163. /// // Create a document using a relative filename.
  1164. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1165. /// {
  1166. /// // Create a text formatting.
  1167. /// Formatting f = new Formatting();
  1168. /// f.FontColor = Color.Red;
  1169. /// f.Size = 30;
  1170. ///
  1171. /// // Iterate through the Paragraphs in this document.
  1172. /// foreach (Paragraph p in document.Paragraphs)
  1173. /// {
  1174. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1175. /// p.InsertText(0, "Start: ", true, f);
  1176. /// }
  1177. ///
  1178. /// // Save all changes made to this document.
  1179. /// document.Save();
  1180. /// }// Release this document from memory.
  1181. /// </code>
  1182. /// </example>
  1183. /// <example>
  1184. /// Inserting tabs using the \t switch.
  1185. /// <code>
  1186. /// // Create a document using a relative filename.
  1187. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1188. /// {
  1189. /// // Create a text formatting.
  1190. /// Formatting f = new Formatting();
  1191. /// f.FontColor = Color.Red;
  1192. /// f.Size = 30;
  1193. ///
  1194. /// // Iterate through the paragraphs in this document.
  1195. /// foreach (Paragraph p in document.Paragraphs)
  1196. /// {
  1197. /// // Insert the string "\tStart:\t" at the begining of every paragraph and flag it as a change.
  1198. /// p.InsertText(0, "\tStart:\t", true, f);
  1199. /// }
  1200. ///
  1201. /// // Save all changes made to this document.
  1202. /// document.Save();
  1203. /// }// Release this document from memory.
  1204. /// </code>
  1205. /// </example>
  1206. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1207. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1208. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1209. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1210. /// <param name="index">The index position of the insertion.</param>
  1211. /// <param name="value">The System.String to insert.</param>
  1212. /// <param name="trackChanges">Flag this insert as a change.</param>
  1213. /// <param name="formatting">The text formatting.</param>
  1214. public void InsertText(int index, string value, bool trackChanges, Formatting formatting)
  1215. {
  1216. // Timestamp to mark the start of insert
  1217. DateTime now = DateTime.Now;
  1218. DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  1219. // Get the first run effected by this Insert
  1220. Run run = GetFirstRunEffectedByInsert(index);
  1221. if (run == null)
  1222. {
  1223. object insert;
  1224. if (formatting != null)
  1225. insert = DocX.FormatInput(value, formatting.Xml);
  1226. else
  1227. insert = DocX.FormatInput(value, null);
  1228. if (trackChanges)
  1229. insert = CreateEdit(EditType.ins, insert_datetime, insert);
  1230. xml.Add(insert);
  1231. }
  1232. else
  1233. {
  1234. object newRuns;
  1235. if (formatting != null)
  1236. newRuns = DocX.FormatInput(value, formatting.Xml);
  1237. else
  1238. newRuns = DocX.FormatInput(value, run.xml.Element(XName.Get("rPr", DocX.w.NamespaceName)));
  1239. // The parent of this Run
  1240. XElement parentElement = run.xml.Parent;
  1241. switch (parentElement.Name.LocalName)
  1242. {
  1243. case "ins":
  1244. {
  1245. // The datetime that this ins was created
  1246. DateTime parent_ins_date = DateTime.Parse(parentElement.Attribute(XName.Get("date", DocX.w.NamespaceName)).Value);
  1247. /*
  1248. * Special case: You want to track changes,
  1249. * and the first Run effected by this insert
  1250. * has a datetime stamp equal to now.
  1251. */
  1252. if (trackChanges && parent_ins_date.CompareTo(insert_datetime) == 0)
  1253. {
  1254. /*
  1255. * Inserting into a non edit and this special case, is the same procedure.
  1256. */
  1257. goto default;
  1258. }
  1259. /*
  1260. * If not the special case above,
  1261. * then inserting into an ins or a del, is the same procedure.
  1262. */
  1263. goto case "del";
  1264. }
  1265. case "del":
  1266. {
  1267. object insert = newRuns;
  1268. if (trackChanges)
  1269. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1270. // Split this Edit at the point you want to insert
  1271. XElement[] splitEdit = SplitEdit(parentElement, index, EditType.ins);
  1272. // Replace the origional run
  1273. parentElement.ReplaceWith
  1274. (
  1275. splitEdit[0],
  1276. insert,
  1277. splitEdit[1]
  1278. );
  1279. break;
  1280. }
  1281. default:
  1282. {
  1283. object insert = newRuns;
  1284. if (trackChanges && !parentElement.Name.LocalName.Equals("ins"))
  1285. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1286. // Split this run at the point you want to insert
  1287. XElement[] splitRun = Run.SplitRun(run, index);
  1288. // Replace the origional run
  1289. run.xml.ReplaceWith
  1290. (
  1291. splitRun[0],
  1292. insert,
  1293. splitRun[1]
  1294. );
  1295. break;
  1296. }
  1297. }
  1298. }
  1299. // Rebuild the run lookup for this paragraph
  1300. runLookup.Clear();
  1301. BuildRunLookup(xml);
  1302. DocX.RenumberIDs(document);
  1303. }
  1304. /// <summary>
  1305. /// Append text to this Paragraph.
  1306. /// </summary>
  1307. /// <param name="text">The text to append.</param>
  1308. /// <returns>This Paragraph with the new text appened.</returns>
  1309. /// <example>
  1310. /// Add a new Paragraph to this document and then append some text to it.
  1311. /// <code>
  1312. /// // Load a document.
  1313. /// using (DocX document = DocX.Create(@"Test.docx"))
  1314. /// {
  1315. /// // Insert a new Paragraph and Append some text to it.
  1316. /// Paragraph p = document.InsertParagraph().Append("Hello World!!!");
  1317. ///
  1318. /// // Save this document.
  1319. /// document.Save();
  1320. /// }
  1321. /// </code>
  1322. /// </example>
  1323. public Paragraph Append(string text)
  1324. {
  1325. List<XElement> newRuns = DocX.FormatInput(text, null);
  1326. xml.Add(newRuns);
  1327. this.runs = xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Reverse().Take(newRuns.Count()).ToList();
  1328. BuildRunLookup(xml);
  1329. return this;
  1330. }
  1331. /// <summary>
  1332. /// Append text on a new line to this Paragraph.
  1333. /// </summary>
  1334. /// <param name="text">The text to append.</param>
  1335. /// <returns>This Paragraph with the new text appened.</returns>
  1336. /// <example>
  1337. /// Add a new Paragraph to this document and then append a new line with some text to it.
  1338. /// <code>
  1339. /// // Load a document.
  1340. /// using (DocX document = DocX.Create(@"Test.docx"))
  1341. /// {
  1342. /// // Insert a new Paragraph and Append a new line with some text to it.
  1343. /// Paragraph p = document.InsertParagraph().AppendLine("Hello World!!!");
  1344. ///
  1345. /// // Save this document.
  1346. /// document.Save();
  1347. /// }
  1348. /// </code>
  1349. /// </example>
  1350. public Paragraph AppendLine(string text)
  1351. {
  1352. return Append("\n" + text);
  1353. }
  1354. internal void ApplyTextFormattingProperty(XName textFormatPropName, string value, object content)
  1355. {
  1356. foreach (XElement run in runs)
  1357. {
  1358. XElement rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  1359. if (rPr == null)
  1360. {
  1361. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  1362. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  1363. }
  1364. rPr.SetElementValue(textFormatPropName, value);
  1365. XElement last = rPr.Elements().Last();
  1366. last.Add(content);
  1367. }
  1368. BuildRunLookup(xml);
  1369. }
  1370. /// <summary>
  1371. /// For use with Append() and AppendLine()
  1372. /// </summary>
  1373. /// <returns>This Paragraph with the last appended text bold.</returns>
  1374. /// <example>
  1375. /// Append text to this Paragraph and then make it bold.
  1376. /// <code>
  1377. /// // Create a document.
  1378. /// using (DocX document = DocX.Create(@"Test.docx"))
  1379. /// {
  1380. /// // Insert a new Paragraph.
  1381. /// Paragraph p = document.InsertParagraph();
  1382. ///
  1383. /// p.Append("I am ")
  1384. /// .Append("Bold").Bold()
  1385. /// .Append(" I am not");
  1386. ///
  1387. /// // Save this document.
  1388. /// document.Save();
  1389. /// }// Release this document from memory.
  1390. /// </code>
  1391. /// </example>
  1392. public Paragraph Bold()
  1393. {
  1394. ApplyTextFormattingProperty(XName.Get("b", DocX.w.NamespaceName), string.Empty, null);
  1395. return this;
  1396. }
  1397. /// <summary>
  1398. /// For use with Append() and AppendLine()
  1399. /// </summary>
  1400. /// <returns>This Paragraph with the last appended text italic.</returns>
  1401. /// <example>
  1402. /// Append text to this Paragraph and then make it italic.
  1403. /// <code>
  1404. /// // Create a document.
  1405. /// using (DocX document = DocX.Create(@"Test.docx"))
  1406. /// {
  1407. /// // Insert a new Paragraph.
  1408. /// Paragraph p = document.InsertParagraph();
  1409. ///
  1410. /// p.Append("I am ")
  1411. /// .Append("Italic").Italic()
  1412. /// .Append(" I am not");
  1413. ///
  1414. /// // Save this document.
  1415. /// document.Save();
  1416. /// }// Release this document from memory.
  1417. /// </code>
  1418. /// </example>
  1419. public Paragraph Italic()
  1420. {
  1421. ApplyTextFormattingProperty(XName.Get("i", DocX.w.NamespaceName), string.Empty, null);
  1422. return this;
  1423. }
  1424. /// <summary>
  1425. /// For use with Append() and AppendLine()
  1426. /// </summary>
  1427. /// <param name="c">A color to use on the appended text.</param>
  1428. /// <returns>This Paragraph with the last appended text colored.</returns>
  1429. /// <example>
  1430. /// Append text to this Paragraph and then color it.
  1431. /// <code>
  1432. /// // Create a document.
  1433. /// using (DocX document = DocX.Create(@"Test.docx"))
  1434. /// {
  1435. /// // Insert a new Paragraph.
  1436. /// Paragraph p = document.InsertParagraph();
  1437. ///
  1438. /// p.Append("I am ")
  1439. /// .Append("Blue").Color(Color.Blue)
  1440. /// .Append(" I am not");
  1441. ///
  1442. /// // Save this document.
  1443. /// document.Save();
  1444. /// }// Release this document from memory.
  1445. /// </code>
  1446. /// </example>
  1447. public Paragraph Color(Color c)
  1448. {
  1449. ApplyTextFormattingProperty(XName.Get("color", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), c.ToHex()));
  1450. return this;
  1451. }
  1452. /// <summary>
  1453. /// For use with Append() and AppendLine()
  1454. /// </summary>
  1455. /// <param name="underlineStyle">The underline style to use for the appended text.</param>
  1456. /// <returns>This Paragraph with the last appended text underlined.</returns>
  1457. /// <example>
  1458. /// Append text to this Paragraph and then underline it.
  1459. /// <code>
  1460. /// // Create a document.
  1461. /// using (DocX document = DocX.Create(@"Test.docx"))
  1462. /// {
  1463. /// // Insert a new Paragraph.
  1464. /// Paragraph p = document.InsertParagraph();
  1465. ///
  1466. /// p.Append("I am ")
  1467. /// .Append("Underlined").UnderlineStyle(UnderlineStyle.doubleLine)
  1468. /// .Append(" I am not");
  1469. ///
  1470. /// // Save this document.
  1471. /// document.Save();
  1472. /// }// Release this document from memory.
  1473. /// </code>
  1474. /// </example>
  1475. public Paragraph UnderlineStyle(UnderlineStyle underlineStyle)
  1476. {
  1477. string value;
  1478. switch (underlineStyle)
  1479. {
  1480. case Novacode.UnderlineStyle.none: value = string.Empty; break;
  1481. case Novacode.UnderlineStyle.singleLine: value = "single"; break;
  1482. case Novacode.UnderlineStyle.doubleLine: value = "double"; break;
  1483. default: value = underlineStyle.ToString(); break;
  1484. }
  1485. ApplyTextFormattingProperty(XName.Get("u", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), value));
  1486. return this;
  1487. }
  1488. /// <summary>
  1489. /// For use with Append() and AppendLine()
  1490. /// </summary>
  1491. /// <param name="fontSize">The font size to use for the appended text.</param>
  1492. /// <returns>This Paragraph with the last appended text resized.</returns>
  1493. /// <example>
  1494. /// Append text to this Paragraph and then resize it.
  1495. /// <code>
  1496. /// // Create a document.
  1497. /// using (DocX document = DocX.Create(@"Test.docx"))
  1498. /// {
  1499. /// // Insert a new Paragraph.
  1500. /// Paragraph p = document.InsertParagraph();
  1501. ///
  1502. /// p.Append("I am ")
  1503. /// .Append("Big").FontSize(20)
  1504. /// .Append(" I am not");
  1505. ///
  1506. /// // Save this document.
  1507. /// document.Save();
  1508. /// }// Release this document from memory.
  1509. /// </code>
  1510. /// </example>
  1511. public Paragraph FontSize(double fontSize)
  1512. {
  1513. if (fontSize - (int)fontSize == 0)
  1514. {
  1515. if (!(fontSize > 0 && fontSize < 1639))
  1516. throw new ArgumentException("Size", "Value must be in the range 0 - 1638");
  1517. }
  1518. else
  1519. throw new ArgumentException("Size", "Value must be either a whole or half number, examples: 32, 32.5");
  1520. ApplyTextFormattingProperty(XName.Get("sz", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  1521. ApplyTextFormattingProperty(XName.Get("szCs", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  1522. return this;
  1523. }
  1524. /// <summary>
  1525. /// For use with Append() and AppendLine()
  1526. /// </summary>
  1527. /// <param name="fontFamily">The font to use for the appended text.</param>
  1528. /// <returns>This Paragraph with the last appended text's font changed.</returns>
  1529. /// <example>
  1530. /// Append text to this Paragraph and then change its font.
  1531. /// <code>
  1532. /// // Create a document.
  1533. /// using (DocX document = DocX.Create(@"Test.docx"))
  1534. /// {
  1535. /// // Insert a new Paragraph.
  1536. /// Paragraph p = document.InsertParagraph();
  1537. ///
  1538. /// p.Append("I am ")
  1539. /// .Append("Times new roman").Font(new FontFamily("Times new roman"))
  1540. /// .Append(" I am not");
  1541. ///
  1542. /// // Save this document.
  1543. /// document.Save();
  1544. /// }// Release this document from memory.
  1545. /// </code>
  1546. /// </example>
  1547. public Paragraph Font(FontFamily fontFamily)
  1548. {
  1549. ApplyTextFormattingProperty(XName.Get("rFonts", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("ascii", DocX.w.NamespaceName), fontFamily.Name));
  1550. return this;
  1551. }
  1552. /// <summary>
  1553. /// For use with Append() and AppendLine()
  1554. /// </summary>
  1555. /// <param name="capsStyle">The caps style to apply to the last appended text.</param>
  1556. /// <returns>This Paragraph with the last appended text's caps style changed.</returns>
  1557. /// <example>
  1558. /// Append text to this Paragraph and then set it to full caps.
  1559. /// <code>
  1560. /// // Create a document.
  1561. /// using (DocX document = DocX.Create(@"Test.docx"))
  1562. /// {
  1563. /// // Insert a new Paragraph.
  1564. /// Paragraph p = document.InsertParagraph();
  1565. ///
  1566. /// p.Append("I am ")
  1567. /// .Append("Capitalized").CapsStyle(CapsStyle.caps)
  1568. /// .Append(" I am not");
  1569. ///
  1570. /// // Save this document.
  1571. /// document.Save();
  1572. /// }// Release this document from memory.
  1573. /// </code>
  1574. /// </example>
  1575. public Paragraph CapsStyle(CapsStyle capsStyle)
  1576. {
  1577. switch(capsStyle)
  1578. {
  1579. case Novacode.CapsStyle.none:
  1580. break;
  1581. default:
  1582. {
  1583. ApplyTextFormattingProperty(XName.Get(capsStyle.ToString(), DocX.w.NamespaceName), string.Empty, null);
  1584. break;
  1585. }
  1586. }
  1587. return this;
  1588. }
  1589. /// <summary>
  1590. /// For use with Append() and AppendLine()
  1591. /// </summary>
  1592. /// <param name="script">The script style to apply to the last appended text.</param>
  1593. /// <returns>This Paragraph with the last appended text's script style changed.</returns>
  1594. /// <example>
  1595. /// Append text to this Paragraph and then set it to superscript.
  1596. /// <code>
  1597. /// // Create a document.
  1598. /// using (DocX document = DocX.Create(@"Test.docx"))
  1599. /// {
  1600. /// // Insert a new Paragraph.
  1601. /// Paragraph p = document.InsertParagraph();
  1602. ///
  1603. /// p.Append("I am ")
  1604. /// .Append("superscript").Script(Script.superscript)
  1605. /// .Append(" I am not");
  1606. ///
  1607. /// // Save this document.
  1608. /// document.Save();
  1609. /// }// Release this document from memory.
  1610. /// </code>
  1611. /// </example>
  1612. public Paragraph Script(Script script)
  1613. {
  1614. switch (script)
  1615. {
  1616. case Novacode.Script.none:
  1617. break;
  1618. default:
  1619. {
  1620. ApplyTextFormattingProperty(XName.Get("vertAlign", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), script.ToString()));
  1621. break;
  1622. }
  1623. }
  1624. return this;
  1625. }
  1626. /// <summary>
  1627. /// For use with Append() and AppendLine()
  1628. /// </summary>
  1629. ///<param name="highlight">The highlight to apply to the last appended text.</param>
  1630. /// <returns>This Paragraph with the last appended text highlighted.</returns>
  1631. /// <example>
  1632. /// Append text to this Paragraph and then highlight it.
  1633. /// <code>
  1634. /// // Create a document.
  1635. /// using (DocX document = DocX.Create(@"Test.docx"))
  1636. /// {
  1637. /// // Insert a new Paragraph.
  1638. /// Paragraph p = document.InsertParagraph();
  1639. ///
  1640. /// p.Append("I am ")
  1641. /// .Append("highlighted").Highlight(Highlight.green)
  1642. /// .Append(" I am not");
  1643. ///
  1644. /// // Save this document.
  1645. /// document.Save();
  1646. /// }// Release this document from memory.
  1647. /// </code>
  1648. /// </example>
  1649. public Paragraph Highlight(Highlight highlight)
  1650. {
  1651. switch (highlight)
  1652. {
  1653. case Novacode.Highlight.none:
  1654. break;
  1655. default:
  1656. {
  1657. ApplyTextFormattingProperty(XName.Get("highlight", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), highlight.ToString()));
  1658. break;
  1659. }
  1660. }
  1661. return this;
  1662. }
  1663. /// <summary>
  1664. /// For use with Append() and AppendLine()
  1665. /// </summary>
  1666. /// <param name="misc">The miscellaneous property to set.</param>
  1667. /// <returns>This Paragraph with the last appended text changed by a miscellaneous property.</returns>
  1668. /// <example>
  1669. /// Append text to this Paragraph and then apply a miscellaneous property.
  1670. /// <code>
  1671. /// // Create a document.
  1672. /// using (DocX document = DocX.Create(@"Test.docx"))
  1673. /// {
  1674. /// // Insert a new Paragraph.
  1675. /// Paragraph p = document.InsertParagraph();
  1676. ///
  1677. /// p.Append("I am ")
  1678. /// .Append("outlined").Misc(Misc.outline)
  1679. /// .Append(" I am not");
  1680. ///
  1681. /// // Save this document.
  1682. /// document.Save();
  1683. /// }// Release this document from memory.
  1684. /// </code>
  1685. /// </example>
  1686. public Paragraph Misc(Misc misc)
  1687. {
  1688. switch (misc)
  1689. {
  1690. case Novacode.Misc.none:
  1691. break;
  1692. case Novacode.Misc.outlineShadow:
  1693. {
  1694. ApplyTextFormattingProperty(XName.Get("outline", DocX.w.NamespaceName), string.Empty, null);
  1695. ApplyTextFormattingProperty(XName.Get("shadow", DocX.w.NamespaceName), string.Empty, null);
  1696. break;
  1697. }
  1698. case Novacode.Misc.engrave:
  1699. {
  1700. ApplyTextFormattingProperty(XName.Get("imprint", DocX.w.NamespaceName), string.Empty, null);
  1701. break;
  1702. }
  1703. default:
  1704. {
  1705. ApplyTextFormattingProperty(XName.Get(misc.ToString(), DocX.w.NamespaceName), string.Empty, null);
  1706. break;
  1707. }
  1708. }
  1709. return this;
  1710. }
  1711. /// <summary>
  1712. /// For use with Append() and AppendLine()
  1713. /// </summary>
  1714. /// <param name="strikeThrough">The strike through style to used on the last appended text.</param>
  1715. /// <returns>This Paragraph with the last appended text striked.</returns>
  1716. /// <example>
  1717. /// Append text to this Paragraph and then strike it.
  1718. /// <code>
  1719. /// // Create a document.
  1720. /// using (DocX document = DocX.Create(@"Test.docx"))
  1721. /// {
  1722. /// // Insert a new Paragraph.
  1723. /// Paragraph p = document.InsertParagraph();
  1724. ///
  1725. /// p.Append("I am ")
  1726. /// .Append("striked").StrikeThrough(StrikeThrough.doubleStrike)
  1727. /// .Append(" I am not");
  1728. ///
  1729. /// // Save this document.
  1730. /// document.Save();
  1731. /// }// Release this document from memory.
  1732. /// </code>
  1733. /// </example>
  1734. public Paragraph StrikeThrough(StrikeThrough strikeThrough)
  1735. {
  1736. string value;
  1737. switch (strikeThrough)
  1738. {
  1739. case Novacode.StrikeThrough.strike: value = "strike"; break;
  1740. case Novacode.StrikeThrough.doubleStrike: value = "dstrike"; break;
  1741. default: return this;
  1742. }
  1743. ApplyTextFormattingProperty(XName.Get(value, DocX.w.NamespaceName), string.Empty, null);
  1744. return this;
  1745. }
  1746. /// <summary>
  1747. /// For use with Append() and AppendLine()
  1748. /// </summary>
  1749. /// <param name="underlineColor">The underline color to use, if no underline is set, a single line will be used.</param>
  1750. /// <returns>This Paragraph with the last appended text underlined in a color.</returns>
  1751. /// <example>
  1752. /// Append text to this Paragraph and then underline it using a color.
  1753. /// <code>
  1754. /// // Create a document.
  1755. /// using (DocX document = DocX.Create(@"Test.docx"))
  1756. /// {
  1757. /// // Insert a new Paragraph.
  1758. /// Paragraph p = document.InsertParagraph();
  1759. ///
  1760. /// p.Append("I am ")
  1761. /// .Append("color underlined").UnderlineStyle(UnderlineStyle.dotted).UnderlineColor(Color.Orange)
  1762. /// .Append(" I am not");
  1763. ///
  1764. /// // Save this document.
  1765. /// document.Save();
  1766. /// }// Release this document from memory.
  1767. /// </code>
  1768. /// </example>
  1769. public Paragraph UnderlineColor(Color underlineColor)
  1770. {
  1771. foreach (XElement run in runs)
  1772. {
  1773. XElement rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  1774. if (rPr == null)
  1775. {
  1776. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  1777. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  1778. }
  1779. XElement u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  1780. if (u == null)
  1781. {
  1782. rPr.SetElementValue(XName.Get("u", DocX.w.NamespaceName), string.Empty);
  1783. u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  1784. u.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), "single");
  1785. }
  1786. u.SetAttributeValue(XName.Get("color", DocX.w.NamespaceName), underlineColor.ToHex());
  1787. }
  1788. BuildRunLookup(xml);
  1789. return this;
  1790. }
  1791. /// <summary>
  1792. /// For use with Append() and AppendLine()
  1793. /// </summary>
  1794. /// <returns>This Paragraph with the last appended text hidden.</returns>
  1795. /// <example>
  1796. /// Append text to this Paragraph and then hide it.
  1797. /// <code>
  1798. /// // Create a document.
  1799. /// using (DocX document = DocX.Create(@"Test.docx"))
  1800. /// {
  1801. /// // Insert a new Paragraph.
  1802. /// Paragraph p = document.InsertParagraph();
  1803. ///
  1804. /// p.Append("I am ")
  1805. /// .Append("hidden").Hide()
  1806. /// .Append(" I am not");
  1807. ///
  1808. /// // Save this document.
  1809. /// document.Save();
  1810. /// }// Release this document from memory.
  1811. /// </code>
  1812. /// </example>
  1813. public Paragraph Hide()
  1814. {
  1815. ApplyTextFormattingProperty(XName.Get("vanish", DocX.w.NamespaceName), string.Empty, null);
  1816. return this;
  1817. }
  1818. public Paragraph Spacing(double spacing)
  1819. {
  1820. spacing *= 20;
  1821. if (spacing - (int)spacing == 0)
  1822. {
  1823. if (!(spacing > -1585 && spacing < 1585))
  1824. throw new ArgumentException("Spacing", "Value must be in the range: -1584 - 1584");
  1825. }
  1826. else
  1827. throw new ArgumentException("Spacing", "Value must be either a whole or acurate to one decimal, examples: 32, 32.1, 32.2, 32.9");
  1828. ApplyTextFormattingProperty(XName.Get("spacing", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), spacing));
  1829. return this;
  1830. }
  1831. public Paragraph Kerning(int kerning)
  1832. {
  1833. if (!new int?[] { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 }.Contains(kerning))
  1834. throw new ArgumentOutOfRangeException("Kerning", "Value must be one of the following: 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48 or 72");
  1835. ApplyTextFormattingProperty(XName.Get("kern", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), kerning * 2));
  1836. return this;
  1837. }
  1838. public Paragraph Position(double position)
  1839. {
  1840. if (!(position > -1585 && position < 1585))
  1841. throw new ArgumentOutOfRangeException("Position", "Value must be in the range -1585 - 1585");
  1842. ApplyTextFormattingProperty(XName.Get("position", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), position * 2));
  1843. return this;
  1844. }
  1845. public Paragraph PercentageScale(int percentageScale)
  1846. {
  1847. if (!(new int?[] { 200, 150, 100, 90, 80, 66, 50, 33 }).Contains(percentageScale))
  1848. throw new ArgumentOutOfRangeException("PercentageScale", "Value must be one of the following: 200, 150, 100, 90, 80, 66, 50 or 33");
  1849. ApplyTextFormattingProperty(XName.Get("w", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), percentageScale));
  1850. return this;
  1851. }
  1852. /// <summary>
  1853. /// Insert a field of type document property, this field will display the custom property cp, at the end of this paragraph.
  1854. /// </summary>
  1855. /// <param name="cp">The custom property to display.</param>
  1856. /// <param name="f">The formatting to use for this text.</param>
  1857. /// <example>
  1858. /// Create, add and display a custom property in a document.
  1859. /// <code>
  1860. /// // Load a document
  1861. /// using (DocX document = DocX.Create(@"Test.docx"))
  1862. /// {
  1863. /// // Create a custom property.
  1864. /// CustomProperty name = new CustomProperty("name", "Cathal Coffey");
  1865. ///
  1866. /// // Add this custom property to this document.
  1867. /// document.AddCustomProperty(name);
  1868. ///
  1869. /// // Create a text formatting.
  1870. /// Formatting f = new Formatting();
  1871. /// f.Bold = true;
  1872. /// f.Size = 14;
  1873. /// f.StrikeThrough = StrickThrough.strike;
  1874. ///
  1875. /// // Insert a new paragraph.
  1876. /// Paragraph p = document.InsertParagraph("Author: ", false, f);
  1877. ///
  1878. /// // Insert a field of type document property to display the custom property name
  1879. /// p.InsertDocProperty(name, f);
  1880. ///
  1881. /// // Save all changes made to this document.
  1882. /// document.Save();
  1883. /// }// Release this document from memory.
  1884. /// </code>
  1885. /// </example>
  1886. public void InsertDocProperty(CustomProperty cp, Formatting f)
  1887. {
  1888. XElement e = new XElement
  1889. (
  1890. XName.Get("fldSimple", DocX.w.NamespaceName),
  1891. new XAttribute(XName.Get("instr", DocX.w.NamespaceName), string.Format(@"DOCPROPERTY {0} \* MERGEFORMAT", cp.Name)),
  1892. new XElement(XName.Get("r", DocX.w.NamespaceName),
  1893. new XElement(XName.Get("t", DocX.w.NamespaceName), f.Xml, cp.Value))
  1894. );
  1895. xml.Add(e);
  1896. }
  1897. /// <summary>
  1898. /// Insert a field of type document property, this field will display the custom property cp, at the end of this paragraph.
  1899. /// </summary>
  1900. /// <param name="cp">The custom property to display.</param>
  1901. /// <example>
  1902. /// Create, add and display a custom property in a document.
  1903. /// <code>
  1904. /// // Load a document
  1905. /// using (DocX document = DocX.Create(@"Test.docx"))
  1906. /// {
  1907. /// // Create a custom property.
  1908. /// CustomProperty name = new CustomProperty("name", "Cathal Coffey");
  1909. ///
  1910. /// // Add this custom property to this document.
  1911. /// document.AddCustomProperty(name);
  1912. ///
  1913. /// // Insert a new paragraph.
  1914. /// Paragraph p = document.InsertParagraph("Author: ", false);
  1915. ///
  1916. /// // Insert a field of type document property to display the custom property name
  1917. /// p.InsertDocProperty(name);
  1918. ///
  1919. /// // Save all changes made to this document.
  1920. /// document.Save();
  1921. /// }// Release this document from memory.
  1922. /// </code>
  1923. /// </example>
  1924. public void InsertDocProperty(CustomProperty cp)
  1925. {
  1926. InsertDocProperty(cp, new Formatting());
  1927. }
  1928. /// <summary>
  1929. /// Removes characters from a Novacode.DocX.Paragraph.
  1930. /// </summary>
  1931. /// <example>
  1932. /// <code>
  1933. /// // Create a document using a relative filename.
  1934. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1935. /// {
  1936. /// // Iterate through the paragraphs
  1937. /// foreach (Paragraph p in document.Paragraphs)
  1938. /// {
  1939. /// // Remove the first two characters from every paragraph
  1940. /// p.RemoveText(0, 2, false);
  1941. /// }
  1942. ///
  1943. /// // Save all changes made to this document.
  1944. /// document.Save();
  1945. /// }// Release this document from memory.
  1946. /// </code>
  1947. /// </example>
  1948. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1949. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1950. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  1951. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  1952. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  1953. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  1954. /// <param name="index">The position to begin deleting characters.</param>
  1955. /// <param name="count">The number of characters to delete</param>
  1956. /// <param name="trackChanges">Track changes</param>
  1957. public void RemoveText(int index, int count, bool trackChanges)
  1958. {
  1959. // Timestamp to mark the start of insert
  1960. DateTime now = DateTime.Now;
  1961. DateTime remove_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  1962. // The number of characters processed so far
  1963. int processed = 0;
  1964. do
  1965. {
  1966. // Get the first run effected by this Remove
  1967. Run run = GetFirstRunEffectedByEdit(index + processed);
  1968. // The parent of this Run
  1969. XElement parentElement = run.xml.Parent;
  1970. switch (parentElement.Name.LocalName)
  1971. {
  1972. case "ins":
  1973. {
  1974. XElement[] splitEditBefore = SplitEdit(parentElement, index + processed, EditType.del);
  1975. int min = Math.Min(count - processed, run.xml.ElementsAfterSelf().Sum(e => GetElementTextLength(e)));
  1976. XElement[] splitEditAfter = SplitEdit(parentElement, index + processed + min, EditType.del);
  1977. XElement temp = SplitEdit(splitEditBefore[1], index + processed + min, EditType.del)[0];
  1978. object middle = CreateEdit(EditType.del, remove_datetime, temp.Elements());
  1979. processed += GetElementTextLength(middle as XElement);
  1980. if (!trackChanges)
  1981. middle = null;
  1982. parentElement.ReplaceWith
  1983. (
  1984. splitEditBefore[0],
  1985. middle,
  1986. splitEditAfter[1]
  1987. );
  1988. processed += GetElementTextLength(middle as XElement);
  1989. break;
  1990. }
  1991. case "del":
  1992. {
  1993. if (trackChanges)
  1994. {
  1995. // You cannot delete from a deletion, advance processed to the end of this del
  1996. processed += GetElementTextLength(parentElement);
  1997. }
  1998. else
  1999. goto case "ins";
  2000. break;
  2001. }
  2002. default:
  2003. {
  2004. XElement[] splitRunBefore = Run.SplitRun(run, index + processed);
  2005. int min = Math.Min(index + processed + (count - processed), run.EndIndex);
  2006. XElement[] splitRunAfter = Run.SplitRun(run, min);
  2007. object middle = CreateEdit(EditType.del, remove_datetime, new List<XElement>() { Run.SplitRun(new Run(run.StartIndex + GetElementTextLength(splitRunBefore[0]), splitRunBefore[1]), min)[0] });
  2008. processed += GetElementTextLength(middle as XElement);
  2009. if (!trackChanges)
  2010. middle = null;
  2011. run.xml.ReplaceWith
  2012. (
  2013. splitRunBefore[0],
  2014. middle,
  2015. splitRunAfter[1]
  2016. );
  2017. break;
  2018. }
  2019. }
  2020. // If after this remove the parent element is empty, remove it.
  2021. if (GetElementTextLength(parentElement) == 0)
  2022. {
  2023. if (parentElement.Parent != null && parentElement.Parent.Name.LocalName != "tc")
  2024. parentElement.Remove();
  2025. }
  2026. }
  2027. while (processed < count);
  2028. // Rebuild the run lookup
  2029. runLookup.Clear();
  2030. BuildRunLookup(xml);
  2031. DocX.RenumberIDs(document);
  2032. }
  2033. /// <summary>
  2034. /// Removes characters from a Novacode.DocX.Paragraph.
  2035. /// </summary>
  2036. /// <example>
  2037. /// <code>
  2038. /// // Create a document using a relative filename.
  2039. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2040. /// {
  2041. /// // Iterate through the paragraphs
  2042. /// foreach (Paragraph p in document.Paragraphs)
  2043. /// {
  2044. /// // Remove all but the first 2 characters from this Paragraph.
  2045. /// p.RemoveText(2, false);
  2046. /// }
  2047. ///
  2048. /// // Save all changes made to this document.
  2049. /// document.Save();
  2050. /// }// Release this document from memory.
  2051. /// </code>
  2052. /// </example>
  2053. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  2054. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  2055. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2056. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2057. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2058. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2059. /// <param name="index">The position to begin deleting characters.</param>
  2060. /// <param name="trackChanges">Track changes</param>
  2061. public void RemoveText(int index, bool trackChanges)
  2062. {
  2063. RemoveText(index, Text.Length - index, trackChanges);
  2064. }
  2065. /// <summary>
  2066. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  2067. /// </summary>
  2068. /// <example>
  2069. /// <code>
  2070. /// // Create a document using a relative filename.
  2071. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2072. /// {
  2073. /// // Iterate through the paragraphs in this document.
  2074. /// foreach (Paragraph p in document.Paragraphs)
  2075. /// {
  2076. /// // Replace all instances of the string "wrong" with the string "right" and ignore case.
  2077. /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase);
  2078. /// }
  2079. ///
  2080. /// // Save all changes made to this document.
  2081. /// document.Save();
  2082. /// }// Release this document from memory.
  2083. /// </code>
  2084. /// </example>
  2085. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  2086. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  2087. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2088. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2089. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2090. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2091. /// <param name="newValue">A System.String to replace all occurances of oldValue.</param>
  2092. /// <param name="oldValue">A System.String to be replaced.</param>
  2093. /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param>
  2094. /// <param name="trackChanges">Track changes</param>
  2095. public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options)
  2096. {
  2097. ReplaceText(oldValue, newValue, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch);
  2098. }
  2099. /// <summary>
  2100. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  2101. /// </summary>
  2102. /// <example>
  2103. /// <code>
  2104. /// // Create a document using a relative filename.
  2105. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2106. /// {
  2107. /// // The formatting to apply to the inserted text.
  2108. /// Formatting newFormatting = new Formatting();
  2109. /// newFormatting.Size = 22;
  2110. /// newFormatting.UnderlineStyle = UnderlineStyle.dotted;
  2111. /// newFormatting.Bold = true;
  2112. ///
  2113. /// // Iterate through the paragraphs in this document.
  2114. /// foreach (Paragraph p in document.Paragraphs)
  2115. /// {
  2116. /// /*
  2117. /// * Replace all instances of the string "wrong" with the string "right" and ignore case.
  2118. /// * Each inserted instance of "wrong" should use the Formatting newFormatting.
  2119. /// */
  2120. /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting);
  2121. /// }
  2122. ///
  2123. /// // Save all changes made to this document.
  2124. /// document.Save();
  2125. /// }// Release this document from memory.
  2126. /// </code>
  2127. /// </example>
  2128. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  2129. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  2130. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2131. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2132. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2133. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2134. /// <param name="newValue">A System.String to replace all occurances of oldValue.</param>
  2135. /// <param name="oldValue">A System.String to be replaced.</param>
  2136. /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param>
  2137. /// <param name="trackChanges">Track changes</param>
  2138. /// <param name="newFormatting">The formatting to apply to the text being inserted.</param>
  2139. public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options, Formatting newFormatting)
  2140. {
  2141. ReplaceText(oldValue, newValue, trackChanges, options, null, null, MatchFormattingOptions.SubsetMatch);
  2142. }
  2143. /// <summary>
  2144. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  2145. /// </summary>
  2146. /// <example>
  2147. /// <code>
  2148. /// // Load a document using a relative filename.
  2149. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2150. /// {
  2151. /// // The formatting to match.
  2152. /// Formatting matchFormatting = new Formatting();
  2153. /// matchFormatting.Size = 10;
  2154. /// matchFormatting.Italic = true;
  2155. /// matchFormatting.FontFamily = new FontFamily("Times New Roman");
  2156. ///
  2157. /// // The formatting to apply to the inserted text.
  2158. /// Formatting newFormatting = new Formatting();
  2159. /// newFormatting.Size = 22;
  2160. /// newFormatting.UnderlineStyle = UnderlineStyle.dotted;
  2161. /// newFormatting.Bold = true;
  2162. ///
  2163. /// // Iterate through the paragraphs in this document.
  2164. /// foreach (Paragraph p in document.Paragraphs)
  2165. /// {
  2166. /// /*
  2167. /// * Replace all instances of the string "wrong" with the string "right" and ignore case.
  2168. /// * Each inserted instance of "wrong" should use the Formatting newFormatting.
  2169. /// * Only replace an instance of "wrong" if it is Size 10, Italic and Times New Roman.
  2170. /// * SubsetMatch means that the formatting must contain all elements of the match formatting,
  2171. /// * but it can also contain additional formatting for example Color, UnderlineStyle, etc.
  2172. /// * ExactMatch means it must not contain additional formatting.
  2173. /// */
  2174. /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting, matchFormatting, MatchFormattingOptions.SubsetMatch);
  2175. /// }
  2176. ///
  2177. /// // Save all changes made to this document.
  2178. /// document.Save();
  2179. /// }// Release this document from memory.
  2180. /// </code>
  2181. /// </example>
  2182. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  2183. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  2184. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2185. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2186. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2187. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2188. /// <param name="newValue">A System.String to replace all occurances of oldValue.</param>
  2189. /// <param name="oldValue">A System.String to be replaced.</param>
  2190. /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param>
  2191. /// <param name="trackChanges">Track changes</param>
  2192. /// <param name="newFormatting">The formatting to apply to the text being inserted.</param>
  2193. /// <param name="matchFormatting">The formatting that the text must match in order to be replaced.</param>
  2194. /// <param name="fo">How should formatting be matched?</param>
  2195. public void ReplaceText(string oldValue, string newValue, bool trackChanges, RegexOptions options, Formatting newFormatting, Formatting matchFormatting, MatchFormattingOptions fo)
  2196. {
  2197. MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(oldValue), options);
  2198. // Loop through the matches in reverse order
  2199. foreach (Match m in mc.Cast<Match>().Reverse())
  2200. {
  2201. // Assume the formatting matches until proven otherwise.
  2202. bool formattingMatch = true;
  2203. // Does the user want to match formatting?
  2204. if (matchFormatting != null)
  2205. {
  2206. // The number of characters processed so far
  2207. int processed = 0;
  2208. do
  2209. {
  2210. // Get the next run effected
  2211. Run run = GetFirstRunEffectedByEdit(m.Index + processed);
  2212. // Get this runs properties
  2213. XElement rPr = run.xml.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2214. if (rPr == null)
  2215. rPr = new Formatting().Xml;
  2216. /*
  2217. * Make sure that every formatting element in f.xml is also in this run,
  2218. * if this is not true, then their formatting does not match.
  2219. */
  2220. if (!ContainsEveryChildOf(matchFormatting.Xml, rPr, fo))
  2221. {
  2222. formattingMatch = false;
  2223. break;
  2224. }
  2225. // We have processed some characters, so update the counter.
  2226. processed += run.Value.Length;
  2227. } while (processed < m.Length);
  2228. }
  2229. // If the formatting matches, do the replace.
  2230. if(formattingMatch)
  2231. {
  2232. InsertText(m.Index + oldValue.Length, newValue, trackChanges, newFormatting);
  2233. RemoveText(m.Index, m.Length, trackChanges);
  2234. }
  2235. }
  2236. }
  2237. internal bool ContainsEveryChildOf(XElement a, XElement b, MatchFormattingOptions fo)
  2238. {
  2239. foreach (XElement e in a.Elements())
  2240. {
  2241. // If a formatting property has the same name and value, its considered to be equivalent.
  2242. if (!b.Elements(e.Name).Where(bElement => bElement.Value == e.Value).Any())
  2243. return false;
  2244. }
  2245. // If the formatting has to be exact, no additionaly formatting must exist.
  2246. if (fo == MatchFormattingOptions.ExactMatch)
  2247. return a.Elements().Count() == b.Elements().Count();
  2248. return true;
  2249. }
  2250. /// <summary>
  2251. /// Find all instances of a string in this paragraph and return their indexes in a List.
  2252. /// </summary>
  2253. /// <param name="str">The string to find</param>
  2254. /// <returns>A list of indexes.</returns>
  2255. /// <example>
  2256. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  2257. /// <code>
  2258. /// // Load a document
  2259. /// using (DocX document = DocX.Load(@"Test.docx"))
  2260. /// {
  2261. /// // Loop through the paragraphs in this document.
  2262. /// foreach(Paragraph p in document.Paragraphs)
  2263. /// {
  2264. /// // Find all instances of 'go' in this paragraph.
  2265. /// List&lt;int&gt; gos = document.FindAll("go");
  2266. ///
  2267. /// /*
  2268. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  2269. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  2270. /// * in document order, every insert would shift the index of the remaining matches.
  2271. /// */
  2272. /// gos.Reverse();
  2273. /// foreach (int index in gos)
  2274. /// {
  2275. /// p.InsertText(index, "don't ", false);
  2276. /// }
  2277. /// }
  2278. ///
  2279. /// // Save all changes made to this document.
  2280. /// document.Save();
  2281. /// }// Release this document from memory.
  2282. /// </code>
  2283. /// </example>
  2284. public List<int> FindAll(string str)
  2285. {
  2286. return FindAll(str, RegexOptions.None);
  2287. }
  2288. /// <summary>
  2289. /// Find all instances of a string in this paragraph and return their indexes in a List.
  2290. /// </summary>
  2291. /// <param name="str">The string to find</param>
  2292. /// <param name="options">The options to use when finding a string match.</param>
  2293. /// <returns>A list of indexes.</returns>
  2294. /// <example>
  2295. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  2296. /// <code>
  2297. /// // Load a document
  2298. /// using (DocX document = DocX.Load(@"Test.docx"))
  2299. /// {
  2300. /// // Loop through the paragraphs in this document.
  2301. /// foreach(Paragraph p in document.Paragraphs)
  2302. /// {
  2303. /// // Find all instances of 'go' in this paragraph (Ignore case).
  2304. /// List&lt;int&gt; gos = document.FindAll("go", RegexOptions.IgnoreCase);
  2305. ///
  2306. /// /*
  2307. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  2308. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  2309. /// * in document order, every insert would shift the index of the remaining matches.
  2310. /// */
  2311. /// gos.Reverse();
  2312. /// foreach (int index in gos)
  2313. /// {
  2314. /// p.InsertText(index, "don't ", false);
  2315. /// }
  2316. /// }
  2317. ///
  2318. /// // Save all changes made to this document.
  2319. /// document.Save();
  2320. /// }// Release this document from memory.
  2321. /// </code>
  2322. /// </example>
  2323. public List<int> FindAll(string str, RegexOptions options)
  2324. {
  2325. MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(str), options);
  2326. var query =
  2327. (
  2328. from m in mc.Cast<Match>()
  2329. select m.Index
  2330. ).ToList();
  2331. return query;
  2332. }
  2333. /// <summary>
  2334. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  2335. /// </summary>
  2336. /// <example>
  2337. /// <code>
  2338. /// // Create a document using a relative filename.
  2339. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2340. /// {
  2341. /// // Iterate through the paragraphs in this document.
  2342. /// foreach (Paragraph p in document.Paragraphs)
  2343. /// {
  2344. /// // Replace all instances of the string "wrong" with the string "right".
  2345. /// p.ReplaceText("wrong", "right", false);
  2346. /// }
  2347. ///
  2348. /// // Save all changes made to this document.
  2349. /// document.Save();
  2350. /// }// Release this document from memory.
  2351. /// </code>
  2352. /// </example>
  2353. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  2354. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  2355. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2356. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2357. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2358. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2359. /// <param name="newValue">A System.String to replace all occurances of oldValue.</param>
  2360. /// <param name="oldValue">A System.String to be replaced.</param>
  2361. /// <param name="trackChanges">Track changes</param>
  2362. public void ReplaceText(string oldValue, string newValue, bool trackChanges)
  2363. {
  2364. ReplaceText(oldValue, newValue, trackChanges, RegexOptions.None, null, null, MatchFormattingOptions.SubsetMatch);
  2365. }
  2366. }
  2367. }