Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770
  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 : InsertBeforeOrAfter
  18. {
  19. internal PackagePart mainPart;
  20. public PackagePart PackagePart { get { return mainPart; } set { mainPart = value; } }
  21. // The Append family of functions use this List to apply style.
  22. internal List<XElement> runs;
  23. // This paragraphs text alignment
  24. private Alignment alignment;
  25. internal int startIndex, endIndex;
  26. /// <summary>
  27. /// Returns a list of all Pictures in a Paragraph.
  28. /// </summary>
  29. /// <example>
  30. /// Returns a list of all Pictures in a Paragraph.
  31. /// <code>
  32. /// // Create a document.
  33. /// using (DocX document = DocX.Load(@"Test.docx"))
  34. /// {
  35. /// // Get the first Paragraph in a document.
  36. /// Paragraph p = document.Paragraphs[0];
  37. ///
  38. /// // Get all of the Pictures in this Paragraph.
  39. /// List<Picture> pictures = p.Pictures;
  40. ///
  41. /// // Save this document.
  42. /// document.Save();
  43. /// }
  44. /// </code>
  45. /// </example>
  46. public List<Picture> Pictures
  47. {
  48. get
  49. {
  50. List<Picture> pictures =
  51. (
  52. from p in Xml.Descendants()
  53. where (p.Name.LocalName == "drawing")
  54. let id =
  55. (
  56. from e in p.Descendants()
  57. where e.Name.LocalName.Equals("blip")
  58. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
  59. ).SingleOrDefault()
  60. where id != null
  61. let img = new Image(Document, mainPart.GetRelationship(id))
  62. select new Picture(Document, p, img)
  63. ).ToList();
  64. return pictures;
  65. }
  66. }
  67. /// <summary>
  68. /// Returns a list of Hyperlinks in this Paragraph.
  69. /// </summary>
  70. /// <example>
  71. /// <code>
  72. /// // Create a document.
  73. /// using (DocX document = DocX.Load(@"Test.docx"))
  74. /// {
  75. /// // Get the first Paragraph in this document.
  76. /// Paragraph p = document.Paragraphs[0];
  77. ///
  78. /// // Get all of the hyperlinks in this Paragraph.
  79. /// List<Hyperlink> hyperlinks = paragraph.Hyperlinks;
  80. ///
  81. /// // Change the first hyperlinks text and Uri
  82. /// Hyperlink h0 = hyperlinks[0];
  83. /// h0.Text = "DocX";
  84. /// h0.Uri = new Uri("http://docx.codeplex.com");
  85. ///
  86. /// // Save this document.
  87. /// document.Save();
  88. /// }
  89. /// </code>
  90. /// </example>
  91. public List<Hyperlink> Hyperlinks
  92. {
  93. get
  94. {
  95. List<Hyperlink> hyperlinks =
  96. (
  97. from h in Xml.Elements()
  98. where (h.Name.LocalName == "hyperlink")
  99. select new Hyperlink(Document, h)
  100. ).ToList();
  101. return hyperlinks;
  102. }
  103. }
  104. ///<summary>
  105. /// The style name of the paragraph.
  106. ///</summary>
  107. public string StyleName
  108. {
  109. get
  110. {
  111. var element = this.GetOrCreate_pPr();
  112. var styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  113. if (styleElement != null)
  114. {
  115. var attr = styleElement.Attribute(XName.Get("val", DocX.w.NamespaceName));
  116. if (attr != null && !string.IsNullOrEmpty(attr.Value))
  117. {
  118. return attr.Value;
  119. }
  120. }
  121. return "Normal";
  122. }
  123. set
  124. {
  125. if (string.IsNullOrEmpty(value))
  126. {
  127. value = "Normal";
  128. }
  129. var element = this.GetOrCreate_pPr();
  130. var styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  131. if (styleElement == null)
  132. {
  133. element.Add(new XElement(XName.Get("pStyle", DocX.w.NamespaceName)));
  134. styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  135. }
  136. styleElement.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), value);
  137. }
  138. }
  139. // A collection of field type DocProperty.
  140. private List<DocProperty> docProperties;
  141. internal List<XElement> styles = new List<XElement>();
  142. /// <summary>
  143. /// Returns a list of field type DocProperty in this document.
  144. /// </summary>
  145. public List<DocProperty> DocumentProperties
  146. {
  147. get { return docProperties; }
  148. }
  149. internal Paragraph(DocX document, XElement xml, int startIndex):base(document, xml)
  150. {
  151. this.startIndex = startIndex;
  152. this.endIndex = startIndex + GetElementTextLength(xml);
  153. RebuildDocProperties();
  154. #region It's possible that a Paragraph may have pStyle references
  155. // Check if this Paragraph references any pStyle elements.
  156. var stylesElements = xml.Descendants(XName.Get("pStyle", DocX.w.NamespaceName));
  157. // If one or more pStyles are referenced.
  158. if (stylesElements.Count() > 0)
  159. {
  160. Uri style_package_uri = new Uri("/word/styles.xml", UriKind.Relative);
  161. PackagePart styles_document = document.package.GetPart(style_package_uri);
  162. using (TextReader tr = new StreamReader(styles_document.GetStream()))
  163. {
  164. XDocument style_document = XDocument.Load(tr);
  165. XElement styles_element = style_document.Element(XName.Get("styles", DocX.w.NamespaceName));
  166. var styles_element_ids = stylesElements.Select(e => e.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value);
  167. //foreach(string id in styles_element_ids)
  168. //{
  169. // var style =
  170. // (
  171. // from d in styles_element.Descendants()
  172. // let styleId = d.Attribute(XName.Get("styleId", DocX.w.NamespaceName))
  173. // let type = d.Attribute(XName.Get("type", DocX.w.NamespaceName))
  174. // where type != null && type.Value == "paragraph" && styleId != null && styleId.Value == id
  175. // select d
  176. // ).First();
  177. // styles.Add(style);
  178. //}
  179. }
  180. }
  181. #endregion
  182. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  183. }
  184. /// <summary>
  185. /// Insert a new Table before this Paragraph, this Table can be from this document or another document.
  186. /// </summary>
  187. /// <param name="t">The Table t to be inserted.</param>
  188. /// <returns>A new Table inserted before this Paragraph.</returns>
  189. /// <example>
  190. /// Insert a new Table before this Paragraph.
  191. /// <code>
  192. /// // Place holder for a Table.
  193. /// Table t;
  194. ///
  195. /// // Load document a.
  196. /// using (DocX documentA = DocX.Load(@"a.docx"))
  197. /// {
  198. /// // Get the first Table from this document.
  199. /// t = documentA.Tables[0];
  200. /// }
  201. ///
  202. /// // Load document b.
  203. /// using (DocX documentB = DocX.Load(@"b.docx"))
  204. /// {
  205. /// // Get the first Paragraph in document b.
  206. /// Paragraph p2 = documentB.Paragraphs[0];
  207. ///
  208. /// // Insert the Table from document a before this Paragraph.
  209. /// Table newTable = p2.InsertTableBeforeSelf(t);
  210. ///
  211. /// // Save all changes made to document b.
  212. /// documentB.Save();
  213. /// }// Release this document from memory.
  214. /// </code>
  215. /// </example>
  216. public override Table InsertTableBeforeSelf(Table t)
  217. {
  218. t = base.InsertTableBeforeSelf(t);
  219. t.mainPart = mainPart;
  220. return t;
  221. }
  222. private Direction direction;
  223. /// <summary>
  224. /// Gets or Sets the Direction of content in this Paragraph.
  225. /// <example>
  226. /// Create a Paragraph with content that flows right to left. Default is left to right.
  227. /// <code>
  228. /// // Create a new document.
  229. /// using (DocX document = DocX.Create("Test.docx"))
  230. /// {
  231. /// // Create a new Paragraph with the text "Hello World".
  232. /// Paragraph p = document.InsertParagraph("Hello World.");
  233. ///
  234. /// // Make this Paragraph flow right to left. Default is left to right.
  235. /// p.Direction = Direction.RightToLeft;
  236. ///
  237. /// // Save all changes made to this document.
  238. /// document.Save();
  239. /// }
  240. /// </code>
  241. /// </example>
  242. /// </summary>
  243. public Direction Direction
  244. {
  245. get
  246. {
  247. XElement pPr = GetOrCreate_pPr();
  248. XElement bidi = pPr.Element(XName.Get("bidi", DocX.w.NamespaceName));
  249. if (bidi == null)
  250. return Direction.LeftToRight;
  251. else
  252. return Direction.RightToLeft;
  253. }
  254. set
  255. {
  256. direction = value;
  257. XElement pPr = GetOrCreate_pPr();
  258. XElement bidi = pPr.Element(XName.Get("bidi", DocX.w.NamespaceName));
  259. if (direction == Direction.RightToLeft)
  260. {
  261. if(bidi == null)
  262. pPr.Add(new XElement(XName.Get("bidi", DocX.w.NamespaceName)));
  263. }
  264. else
  265. {
  266. if (bidi != null)
  267. bidi.Remove();
  268. }
  269. }
  270. }
  271. /// <summary>
  272. /// If the pPr element doesent exist it is created, either way it is returned by this function.
  273. /// </summary>
  274. /// <returns>The pPr element for this Paragraph.</returns>
  275. internal XElement GetOrCreate_pPr()
  276. {
  277. // Get the element.
  278. XElement pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  279. // If it dosen't exist, create it.
  280. if (pPr == null)
  281. {
  282. Xml.AddFirst(new XElement(XName.Get("pPr", DocX.w.NamespaceName)));
  283. pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  284. }
  285. // Return the pPr element for this Paragraph.
  286. return pPr;
  287. }
  288. /// <summary>
  289. /// If the ind element doesent exist it is created, either way it is returned by this function.
  290. /// </summary>
  291. /// <returns>The ind element for this Paragraphs pPr.</returns>
  292. internal XElement GetOrCreate_pPr_ind()
  293. {
  294. // Get the element.
  295. XElement pPr = GetOrCreate_pPr();
  296. XElement ind = pPr.Element(XName.Get("ind", DocX.w.NamespaceName));
  297. // If it dosen't exist, create it.
  298. if (ind == null)
  299. {
  300. pPr.Add(new XElement(XName.Get("ind", DocX.w.NamespaceName)));
  301. ind = pPr.Element(XName.Get("ind", DocX.w.NamespaceName));
  302. }
  303. // Return the pPr element for this Paragraph.
  304. return ind;
  305. }
  306. private float indentationFirstLine;
  307. /// <summary>
  308. /// Get or set the indentation of the first line of this Paragraph.
  309. /// </summary>
  310. /// <example>
  311. /// Indent only the first line of a Paragraph.
  312. /// <code>
  313. /// // Create a new document.
  314. /// using (DocX document = DocX.Create("Test.docx"))
  315. /// {
  316. /// // Create a new Paragraph.
  317. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  318. ///
  319. /// // Indent only the first line of the Paragraph.
  320. /// p.IndentationFirstLine = 2.0f;
  321. ///
  322. /// // Save all changes made to this document.
  323. /// document.Save();
  324. /// }
  325. /// </code>
  326. /// </example>
  327. public float IndentationFirstLine
  328. {
  329. get
  330. {
  331. XElement pPr = GetOrCreate_pPr();
  332. XElement ind = GetOrCreate_pPr_ind();
  333. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  334. if (firstLine != null)
  335. return float.Parse(firstLine.Value);
  336. return 0.0f;
  337. }
  338. set
  339. {
  340. if (IndentationFirstLine != value)
  341. {
  342. indentationFirstLine = value;
  343. XElement pPr = GetOrCreate_pPr();
  344. XElement ind = GetOrCreate_pPr_ind();
  345. // Paragraph can either be firstLine or hanging (Remove hanging).
  346. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  347. if (hanging != null)
  348. hanging.Remove();
  349. string indentation = ((indentationFirstLine / 0.1) * 57).ToString();
  350. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  351. if (firstLine != null)
  352. firstLine.Value = indentation;
  353. else
  354. ind.Add(new XAttribute(XName.Get("firstLine", DocX.w.NamespaceName), indentation));
  355. }
  356. }
  357. }
  358. private float indentationHanging;
  359. /// <summary>
  360. /// Get or set the indentation of all but the first line of this Paragraph.
  361. /// </summary>
  362. /// <example>
  363. /// Indent all but the first line of a Paragraph.
  364. /// <code>
  365. /// // Create a new document.
  366. /// using (DocX document = DocX.Create("Test.docx"))
  367. /// {
  368. /// // Create a new Paragraph.
  369. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  370. ///
  371. /// // Indent all but the first line of the Paragraph.
  372. /// p.IndentationHanging = 1.0f;
  373. ///
  374. /// // Save all changes made to this document.
  375. /// document.Save();
  376. /// }
  377. /// </code>
  378. /// </example>
  379. public float IndentationHanging
  380. {
  381. get
  382. {
  383. XElement pPr = GetOrCreate_pPr();
  384. XElement ind = GetOrCreate_pPr_ind();
  385. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  386. if (hanging != null)
  387. return float.Parse(hanging.Value);
  388. return 0.0f;
  389. }
  390. set
  391. {
  392. if (IndentationHanging != value)
  393. {
  394. indentationHanging = value;
  395. XElement pPr = GetOrCreate_pPr();
  396. XElement ind = GetOrCreate_pPr_ind();
  397. // Paragraph can either be firstLine or hanging (Remove firstLine).
  398. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  399. if (firstLine != null)
  400. firstLine.Remove();
  401. string indentation = ((indentationHanging / 0.1) * 57).ToString();
  402. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  403. if (hanging != null)
  404. hanging.Value = indentation;
  405. else
  406. ind.Add(new XAttribute(XName.Get("hanging", DocX.w.NamespaceName), indentation));
  407. }
  408. }
  409. }
  410. private float indentationBefore;
  411. /// <summary>
  412. /// Set the before indentation in cm for this Paragraph.
  413. /// </summary>
  414. /// <example>
  415. /// // Indent an entire Paragraph from the left.
  416. /// <code>
  417. /// // Create a new document.
  418. /// using (DocX document = DocX.Create("Test.docx"))
  419. /// {
  420. /// // Create a new Paragraph.
  421. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  422. ///
  423. /// // Indent this entire Paragraph from the left.
  424. /// p.IndentationBefore = 2.0f;
  425. ///
  426. /// // Save all changes made to this document.
  427. /// document.Save();
  428. ///}
  429. /// </code>
  430. /// </example>
  431. public float IndentationBefore
  432. {
  433. get
  434. {
  435. XElement pPr = GetOrCreate_pPr();
  436. XElement ind = GetOrCreate_pPr_ind();
  437. XAttribute left = ind.Attribute(XName.Get("left", DocX.w.NamespaceName));
  438. if (left != null)
  439. return float.Parse(left.Value);
  440. return 0.0f;
  441. }
  442. set
  443. {
  444. if (IndentationBefore != value)
  445. {
  446. indentationBefore = value;
  447. XElement pPr = GetOrCreate_pPr();
  448. XElement ind = GetOrCreate_pPr_ind();
  449. string indentation = ((indentationBefore / 0.1) * 57).ToString();
  450. XAttribute left = ind.Attribute(XName.Get("left", DocX.w.NamespaceName));
  451. if(left != null)
  452. left.Value = indentation;
  453. else
  454. ind.Add(new XAttribute(XName.Get("left", DocX.w.NamespaceName), indentation));
  455. }
  456. }
  457. }
  458. private float indentationAfter = 0.0f;
  459. /// <summary>
  460. /// Set the after indentation in cm for this Paragraph.
  461. /// </summary>
  462. /// <example>
  463. /// // Indent an entire Paragraph from the right.
  464. /// <code>
  465. /// // Create a new document.
  466. /// using (DocX document = DocX.Create("Test.docx"))
  467. /// {
  468. /// // Create a new Paragraph.
  469. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  470. ///
  471. /// // Make the content of this Paragraph flow right to left.
  472. /// p.Direction = Direction.RightToLeft;
  473. ///
  474. /// // Indent this entire Paragraph from the right.
  475. /// p.IndentationAfter = 2.0f;
  476. ///
  477. /// // Save all changes made to this document.
  478. /// document.Save();
  479. /// }
  480. /// </code>
  481. /// </example>
  482. public float IndentationAfter
  483. {
  484. get
  485. {
  486. XElement pPr = GetOrCreate_pPr();
  487. XElement ind = GetOrCreate_pPr_ind();
  488. XAttribute right = ind.Attribute(XName.Get("right", DocX.w.NamespaceName));
  489. if (right != null)
  490. return float.Parse(right.Value);
  491. return 0.0f;
  492. }
  493. set
  494. {
  495. if (IndentationAfter != value)
  496. {
  497. indentationAfter = value;
  498. XElement pPr = GetOrCreate_pPr();
  499. XElement ind = GetOrCreate_pPr_ind();
  500. string indentation = ((indentationAfter / 0.1) * 57).ToString();
  501. XAttribute right = ind.Attribute(XName.Get("right", DocX.w.NamespaceName));
  502. if (right != null)
  503. right.Value = indentation;
  504. else
  505. ind.Add(new XAttribute(XName.Get("right", DocX.w.NamespaceName), indentation));
  506. }
  507. }
  508. }
  509. /// <summary>
  510. /// Insert a new Table into this document before this Paragraph.
  511. /// </summary>
  512. /// <param name="rowCount">The number of rows this Table should have.</param>
  513. /// <param name="coloumnCount">The number of coloumns this Table should have.</param>
  514. /// <returns>A new Table inserted before this Paragraph.</returns>
  515. /// <example>
  516. /// <code>
  517. /// // Create a new document.
  518. /// using (DocX document = DocX.Create(@"Test.docx"))
  519. /// {
  520. /// //Insert a Paragraph into this document.
  521. /// Paragraph p = document.InsertParagraph("Hello World", false);
  522. ///
  523. /// // Insert a new Table before this Paragraph.
  524. /// Table newTable = p.InsertTableBeforeSelf(2, 2);
  525. /// newTable.Design = TableDesign.LightShadingAccent2;
  526. /// newTable.Alignment = Alignment.center;
  527. ///
  528. /// // Save all changes made to this document.
  529. /// document.Save();
  530. /// }// Release this document from memory.
  531. /// </code>
  532. /// </example>
  533. public override Table InsertTableBeforeSelf(int rowCount, int coloumnCount)
  534. {
  535. return base.InsertTableBeforeSelf(rowCount, coloumnCount);
  536. }
  537. /// <summary>
  538. /// Insert a new Table after this Paragraph.
  539. /// </summary>
  540. /// <param name="t">The Table t to be inserted.</param>
  541. /// <returns>A new Table inserted after this Paragraph.</returns>
  542. /// <example>
  543. /// Insert a new Table after this Paragraph.
  544. /// <code>
  545. /// // Place holder for a Table.
  546. /// Table t;
  547. ///
  548. /// // Load document a.
  549. /// using (DocX documentA = DocX.Load(@"a.docx"))
  550. /// {
  551. /// // Get the first Table from this document.
  552. /// t = documentA.Tables[0];
  553. /// }
  554. ///
  555. /// // Load document b.
  556. /// using (DocX documentB = DocX.Load(@"b.docx"))
  557. /// {
  558. /// // Get the first Paragraph in document b.
  559. /// Paragraph p2 = documentB.Paragraphs[0];
  560. ///
  561. /// // Insert the Table from document a after this Paragraph.
  562. /// Table newTable = p2.InsertTableAfterSelf(t);
  563. ///
  564. /// // Save all changes made to document b.
  565. /// documentB.Save();
  566. /// }// Release this document from memory.
  567. /// </code>
  568. /// </example>
  569. public override Table InsertTableAfterSelf(Table t)
  570. {
  571. t = base.InsertTableAfterSelf(t);
  572. t.mainPart = mainPart;
  573. return t;
  574. }
  575. /// <summary>
  576. /// Insert a new Table into this document after this Paragraph.
  577. /// </summary>
  578. /// <param name="rowCount">The number of rows this Table should have.</param>
  579. /// <param name="coloumnCount">The number of coloumns this Table should have.</param>
  580. /// <returns>A new Table inserted after this Paragraph.</returns>
  581. /// <example>
  582. /// <code>
  583. /// // Create a new document.
  584. /// using (DocX document = DocX.Create(@"Test.docx"))
  585. /// {
  586. /// //Insert a Paragraph into this document.
  587. /// Paragraph p = document.InsertParagraph("Hello World", false);
  588. ///
  589. /// // Insert a new Table after this Paragraph.
  590. /// Table newTable = p.InsertTableAfterSelf(2, 2);
  591. /// newTable.Design = TableDesign.LightShadingAccent2;
  592. /// newTable.Alignment = Alignment.center;
  593. ///
  594. /// // Save all changes made to this document.
  595. /// document.Save();
  596. /// }// Release this document from memory.
  597. /// </code>
  598. /// </example>
  599. public override Table InsertTableAfterSelf(int rowCount, int coloumnCount)
  600. {
  601. return base.InsertTableAfterSelf(rowCount, coloumnCount);
  602. }
  603. /// <summary>
  604. /// Insert a Paragraph before this Paragraph, this Paragraph may have come from the same or another document.
  605. /// </summary>
  606. /// <param name="p">The Paragraph to insert.</param>
  607. /// <returns>The Paragraph now associated with this document.</returns>
  608. /// <example>
  609. /// Take a Paragraph from document a, and insert it into document b before this Paragraph.
  610. /// <code>
  611. /// // Place holder for a Paragraph.
  612. /// Paragraph p;
  613. ///
  614. /// // Load document a.
  615. /// using (DocX documentA = DocX.Load(@"a.docx"))
  616. /// {
  617. /// // Get the first paragraph from this document.
  618. /// p = documentA.Paragraphs[0];
  619. /// }
  620. ///
  621. /// // Load document b.
  622. /// using (DocX documentB = DocX.Load(@"b.docx"))
  623. /// {
  624. /// // Get the first Paragraph in document b.
  625. /// Paragraph p2 = documentB.Paragraphs[0];
  626. ///
  627. /// // Insert the Paragraph from document a before this Paragraph.
  628. /// Paragraph newParagraph = p2.InsertParagraphBeforeSelf(p);
  629. ///
  630. /// // Save all changes made to document b.
  631. /// documentB.Save();
  632. /// }// Release this document from memory.
  633. /// </code>
  634. /// </example>
  635. public override Paragraph InsertParagraphBeforeSelf(Paragraph p)
  636. {
  637. Paragraph p2 = base.InsertParagraphBeforeSelf(p);
  638. p2.PackagePart = mainPart;
  639. return p2;
  640. }
  641. /// <summary>
  642. /// Insert a new Paragraph before this Paragraph.
  643. /// </summary>
  644. /// <param name="text">The initial text for this new Paragraph.</param>
  645. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  646. /// <example>
  647. /// Insert a new paragraph before the first Paragraph in this document.
  648. /// <code>
  649. /// // Create a new document.
  650. /// using (DocX document = DocX.Create(@"Test.docx"))
  651. /// {
  652. /// // Insert a Paragraph into this document.
  653. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  654. ///
  655. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.");
  656. ///
  657. /// // Save all changes made to this new document.
  658. /// document.Save();
  659. /// }// Release this new document form memory.
  660. /// </code>
  661. /// </example>
  662. public override Paragraph InsertParagraphBeforeSelf(string text)
  663. {
  664. Paragraph p = base.InsertParagraphBeforeSelf(text);
  665. p.PackagePart = mainPart;
  666. return p;
  667. }
  668. /// <summary>
  669. /// Insert a new Paragraph before this Paragraph.
  670. /// </summary>
  671. /// <param name="text">The initial text for this new Paragraph.</param>
  672. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  673. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  674. /// <example>
  675. /// Insert a new paragraph before the first Paragraph in this document.
  676. /// <code>
  677. /// // Create a new document.
  678. /// using (DocX document = DocX.Create(@"Test.docx"))
  679. /// {
  680. /// // Insert a Paragraph into this document.
  681. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  682. ///
  683. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false);
  684. ///
  685. /// // Save all changes made to this new document.
  686. /// document.Save();
  687. /// }// Release this new document form memory.
  688. /// </code>
  689. /// </example>
  690. public override Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges)
  691. {
  692. Paragraph p = base.InsertParagraphBeforeSelf(text, trackChanges);
  693. p.PackagePart = mainPart;
  694. return p;
  695. }
  696. /// <summary>
  697. /// Insert a new Paragraph before this Paragraph.
  698. /// </summary>
  699. /// <param name="text">The initial text for this new Paragraph.</param>
  700. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  701. /// <param name="formatting">The formatting to apply to this insertion.</param>
  702. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  703. /// <example>
  704. /// Insert a new paragraph before the first Paragraph in this document.
  705. /// <code>
  706. /// // Create a new document.
  707. /// using (DocX document = DocX.Create(@"Test.docx"))
  708. /// {
  709. /// // Insert a Paragraph into this document.
  710. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  711. ///
  712. /// Formatting boldFormatting = new Formatting();
  713. /// boldFormatting.Bold = true;
  714. ///
  715. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false, boldFormatting);
  716. ///
  717. /// // Save all changes made to this new document.
  718. /// document.Save();
  719. /// }// Release this new document form memory.
  720. /// </code>
  721. /// </example>
  722. public override Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges, Formatting formatting)
  723. {
  724. Paragraph p = base.InsertParagraphBeforeSelf(text, trackChanges, formatting);
  725. p.PackagePart = mainPart;
  726. return p;
  727. }
  728. /// <summary>
  729. /// Insert a page break before a Paragraph.
  730. /// </summary>
  731. /// <example>
  732. /// Insert 2 Paragraphs into a document with a page break between them.
  733. /// <code>
  734. /// using (DocX document = DocX.Create(@"Test.docx"))
  735. /// {
  736. /// // Insert a new Paragraph.
  737. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  738. ///
  739. /// // Insert a new Paragraph.
  740. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  741. ///
  742. /// // Insert a page break before Paragraph two.
  743. /// p2.InsertPageBreakBeforeSelf();
  744. ///
  745. /// // Save this document.
  746. /// document.Save();
  747. /// }// Release this document from memory.
  748. /// </code>
  749. /// </example>
  750. public override void InsertPageBreakBeforeSelf()
  751. {
  752. base.InsertPageBreakBeforeSelf();
  753. }
  754. /// <summary>
  755. /// Insert a page break after a Paragraph.
  756. /// </summary>
  757. /// <example>
  758. /// Insert 2 Paragraphs into a document with a page break between them.
  759. /// <code>
  760. /// using (DocX document = DocX.Create(@"Test.docx"))
  761. /// {
  762. /// // Insert a new Paragraph.
  763. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  764. ///
  765. /// // Insert a page break after this Paragraph.
  766. /// p1.InsertPageBreakAfterSelf();
  767. ///
  768. /// // Insert a new Paragraph.
  769. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  770. ///
  771. /// // Save this document.
  772. /// document.Save();
  773. /// }// Release this document from memory.
  774. /// </code>
  775. /// </example>
  776. public override void InsertPageBreakAfterSelf()
  777. {
  778. base.InsertPageBreakAfterSelf();
  779. }
  780. [Obsolete("Instead use: InsertHyperlink(Hyperlink h, int index)")]
  781. public Paragraph InsertHyperlink(int index, Hyperlink h) { return InsertHyperlink(h, index); }
  782. /// <summary>
  783. /// This function inserts a hyperlink into a Paragraph at a specified character index.
  784. /// </summary>
  785. /// <param name="index">The index to insert at.</param>
  786. /// <param name="h">The hyperlink to insert.</param>
  787. /// <returns>The Paragraph with the Hyperlink inserted at the specified index.</returns>
  788. /// <!--
  789. /// This function was added by Brian Campbell aka chickendelicious on Jun 16 2010
  790. /// Thank you Brian.
  791. /// -->
  792. public Paragraph InsertHyperlink(Hyperlink h, int index = 0)
  793. {
  794. // Convert the path of this mainPart to its equilivant rels file path.
  795. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  796. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  797. // Check to see if the rels file exists and create it if not.
  798. if (!Document.package.PartExists(rels_path))
  799. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  800. // Check to see if a rel for this Picture exists, create it if not.
  801. var Id = GetOrGenerateRel(h);
  802. XElement h_xml;
  803. if (index == 0)
  804. {
  805. // Add this hyperlink as the last element.
  806. Xml.AddFirst(h.Xml);
  807. // Extract the picture back out of the DOM.
  808. h_xml = (XElement)Xml.FirstNode;
  809. }
  810. else
  811. {
  812. // Get the first run effected by this Insert
  813. Run run = GetFirstRunEffectedByEdit(index);
  814. if (run == null)
  815. {
  816. // Add this hyperlink as the last element.
  817. Xml.Add(h.Xml);
  818. // Extract the picture back out of the DOM.
  819. h_xml = (XElement)Xml.LastNode;
  820. }
  821. else
  822. {
  823. // Split this run at the point you want to insert
  824. XElement[] splitRun = Run.SplitRun(run, index);
  825. // Replace the origional run.
  826. run.Xml.ReplaceWith
  827. (
  828. splitRun[0],
  829. h.Xml,
  830. splitRun[1]
  831. );
  832. // Get the first run effected by this Insert
  833. run = GetFirstRunEffectedByEdit(index);
  834. // The picture has to be the next element, extract it back out of the DOM.
  835. h_xml = (XElement)run.Xml.NextNode;
  836. }
  837. h_xml.SetAttributeValue(DocX.r + "id", Id);
  838. }
  839. return this;
  840. }
  841. /// <summary>
  842. /// Remove the Hyperlink at the provided index. The first hyperlink is at index 0.
  843. /// Using a negative index or an index greater than the index of the last hyperlink will cause an ArgumentOutOfRangeException() to be thrown.
  844. /// </summary>
  845. /// <param name="index">The index of the hyperlink to be removed.</param>
  846. /// <example>
  847. /// <code>
  848. /// // Crete a new document.
  849. /// using (DocX document = DocX.Create("Test.docx"))
  850. /// {
  851. /// // Add a Hyperlink into this document.
  852. /// Hyperlink h = document.AddHyperlink("link", new Uri("http://www.google.com"));
  853. ///
  854. /// // Insert a new Paragraph into the document.
  855. /// Paragraph p1 = document.InsertParagraph("AC");
  856. ///
  857. /// // Insert the hyperlink into this Paragraph.
  858. /// p1.InsertHyperlink(1, h);
  859. /// Assert.IsTrue(p1.Text == "AlinkC"); // Make sure the hyperlink was inserted correctly;
  860. ///
  861. /// // Remove the hyperlink
  862. /// p1.RemoveHyperlink(0);
  863. /// Assert.IsTrue(p1.Text == "AC"); // Make sure the hyperlink was removed correctly;
  864. /// }
  865. /// </code>
  866. /// </example>
  867. public void RemoveHyperlink(int index)
  868. {
  869. // Dosen't make sense to remove a Hyperlink at a negative index.
  870. if (index < 0)
  871. throw new ArgumentOutOfRangeException();
  872. // Need somewhere to store the count.
  873. int count = 0;
  874. bool found = false;
  875. RemoveHyperlinkRecursive(Xml, index, ref count, ref found);
  876. // If !found then the user tried to remove a hyperlink at an index greater than the last.
  877. if (!found)
  878. throw new ArgumentOutOfRangeException();
  879. }
  880. internal void RemoveHyperlinkRecursive(XElement xml, int index, ref int count, ref bool found)
  881. {
  882. if (xml.Name.LocalName.Equals("hyperlink", StringComparison.CurrentCultureIgnoreCase))
  883. {
  884. // This is the hyperlink to be removed.
  885. if (count == index)
  886. {
  887. found = true;
  888. xml.Remove();
  889. }
  890. else
  891. count++;
  892. }
  893. if (xml.HasElements)
  894. foreach (XElement e in xml.Elements())
  895. if (!found)
  896. RemoveHyperlinkRecursive(e, index, ref count, ref found);
  897. }
  898. /// <summary>
  899. /// Insert a Paragraph after this Paragraph, this Paragraph may have come from the same or another document.
  900. /// </summary>
  901. /// <param name="p">The Paragraph to insert.</param>
  902. /// <returns>The Paragraph now associated with this document.</returns>
  903. /// <example>
  904. /// Take a Paragraph from document a, and insert it into document b after this Paragraph.
  905. /// <code>
  906. /// // Place holder for a Paragraph.
  907. /// Paragraph p;
  908. ///
  909. /// // Load document a.
  910. /// using (DocX documentA = DocX.Load(@"a.docx"))
  911. /// {
  912. /// // Get the first paragraph from this document.
  913. /// p = documentA.Paragraphs[0];
  914. /// }
  915. ///
  916. /// // Load document b.
  917. /// using (DocX documentB = DocX.Load(@"b.docx"))
  918. /// {
  919. /// // Get the first Paragraph in document b.
  920. /// Paragraph p2 = documentB.Paragraphs[0];
  921. ///
  922. /// // Insert the Paragraph from document a after this Paragraph.
  923. /// Paragraph newParagraph = p2.InsertParagraphAfterSelf(p);
  924. ///
  925. /// // Save all changes made to document b.
  926. /// documentB.Save();
  927. /// }// Release this document from memory.
  928. /// </code>
  929. /// </example>
  930. public override Paragraph InsertParagraphAfterSelf(Paragraph p)
  931. {
  932. Paragraph p2 = base.InsertParagraphAfterSelf(p);
  933. p2.PackagePart = mainPart;
  934. return p2;
  935. }
  936. /// <summary>
  937. /// Insert a new Paragraph after this Paragraph.
  938. /// </summary>
  939. /// <param name="text">The initial text for this new Paragraph.</param>
  940. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  941. /// <param name="formatting">The formatting to apply to this insertion.</param>
  942. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  943. /// <example>
  944. /// Insert a new paragraph after the first Paragraph in this document.
  945. /// <code>
  946. /// // Create a new document.
  947. /// using (DocX document = DocX.Create(@"Test.docx"))
  948. /// {
  949. /// // Insert a Paragraph into this document.
  950. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  951. ///
  952. /// Formatting boldFormatting = new Formatting();
  953. /// boldFormatting.Bold = true;
  954. ///
  955. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false, boldFormatting);
  956. ///
  957. /// // Save all changes made to this new document.
  958. /// document.Save();
  959. /// }// Release this new document form memory.
  960. /// </code>
  961. /// </example>
  962. public override Paragraph InsertParagraphAfterSelf(string text, bool trackChanges, Formatting formatting)
  963. {
  964. Paragraph p = base.InsertParagraphAfterSelf(text, trackChanges, formatting);
  965. p.PackagePart = mainPart;
  966. return p;
  967. }
  968. /// <summary>
  969. /// Insert a new Paragraph after this Paragraph.
  970. /// </summary>
  971. /// <param name="text">The initial text for this new Paragraph.</param>
  972. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  973. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  974. /// <example>
  975. /// Insert a new paragraph after the first Paragraph in this document.
  976. /// <code>
  977. /// // Create a new document.
  978. /// using (DocX document = DocX.Create(@"Test.docx"))
  979. /// {
  980. /// // Insert a Paragraph into this document.
  981. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  982. ///
  983. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false);
  984. ///
  985. /// // Save all changes made to this new document.
  986. /// document.Save();
  987. /// }// Release this new document form memory.
  988. /// </code>
  989. /// </example>
  990. public override Paragraph InsertParagraphAfterSelf(string text, bool trackChanges)
  991. {
  992. Paragraph p = base.InsertParagraphAfterSelf(text, trackChanges);
  993. p.PackagePart = mainPart;
  994. return p;
  995. }
  996. /// <summary>
  997. /// Insert a new Paragraph after this Paragraph.
  998. /// </summary>
  999. /// <param name="text">The initial text for this new Paragraph.</param>
  1000. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  1001. /// <example>
  1002. /// Insert a new paragraph after the first Paragraph in this document.
  1003. /// <code>
  1004. /// // Create a new document.
  1005. /// using (DocX document = DocX.Create(@"Test.docx"))
  1006. /// {
  1007. /// // Insert a Paragraph into this document.
  1008. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  1009. ///
  1010. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.");
  1011. ///
  1012. /// // Save all changes made to this new document.
  1013. /// document.Save();
  1014. /// }// Release this new document form memory.
  1015. /// </code>
  1016. /// </example>
  1017. public override Paragraph InsertParagraphAfterSelf(string text)
  1018. {
  1019. Paragraph p = base.InsertParagraphAfterSelf(text);
  1020. p.PackagePart = mainPart;
  1021. return p;
  1022. }
  1023. private void RebuildDocProperties()
  1024. {
  1025. docProperties =
  1026. (
  1027. from xml in Xml.Descendants(XName.Get("fldSimple", DocX.w.NamespaceName))
  1028. select new DocProperty(Document, xml)
  1029. ).ToList();
  1030. }
  1031. /// <summary>
  1032. /// Gets or set this Paragraphs text alignment.
  1033. /// </summary>
  1034. public Alignment Alignment
  1035. {
  1036. get { return alignment; }
  1037. set
  1038. {
  1039. alignment = value;
  1040. XElement pPr = GetOrCreate_pPr();
  1041. XElement jc = pPr.Element(XName.Get("jc", DocX.w.NamespaceName));
  1042. if (alignment != Novacode.Alignment.left)
  1043. {
  1044. if(jc == null)
  1045. pPr.Add(new XElement(XName.Get("jc", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), alignment.ToString())));
  1046. else
  1047. jc.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value = alignment.ToString();
  1048. }
  1049. else
  1050. {
  1051. if (jc != null)
  1052. jc.Remove();
  1053. }
  1054. }
  1055. }
  1056. /// <summary>
  1057. /// Remove this Paragraph from the document.
  1058. /// </summary>
  1059. /// <param name="trackChanges">Should this remove be tracked as a change?</param>
  1060. /// <example>
  1061. /// Remove a Paragraph from a document and track it as a change.
  1062. /// <code>
  1063. /// // Create a document using a relative filename.
  1064. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1065. /// {
  1066. /// // Create and Insert a new Paragraph into this document.
  1067. /// Paragraph p = document.InsertParagraph("Hello", false);
  1068. ///
  1069. /// // Remove the Paragraph and track this as a change.
  1070. /// p.Remove(true);
  1071. ///
  1072. /// // Save all changes made to this document.
  1073. /// document.Save();
  1074. /// }// Release this document from memory.
  1075. /// </code>
  1076. /// </example>
  1077. public void Remove(bool trackChanges)
  1078. {
  1079. if (trackChanges)
  1080. {
  1081. DateTime now = DateTime.Now.ToUniversalTime();
  1082. List<XElement> elements = Xml.Elements().ToList();
  1083. List<XElement> temp = new List<XElement>();
  1084. for (int i = 0; i < elements.Count(); i++ )
  1085. {
  1086. XElement e = elements[i];
  1087. if (e.Name.LocalName != "del")
  1088. {
  1089. temp.Add(e);
  1090. e.Remove();
  1091. }
  1092. else
  1093. {
  1094. if (temp.Count() > 0)
  1095. {
  1096. e.AddBeforeSelf(CreateEdit(EditType.del, now, temp.Elements()));
  1097. temp.Clear();
  1098. }
  1099. }
  1100. }
  1101. if (temp.Count() > 0)
  1102. Xml.Add(CreateEdit(EditType.del, now, temp));
  1103. }
  1104. else
  1105. {
  1106. // If this is the only Paragraph in the Cell then we cannot remove it.
  1107. if (Xml.Parent.Name.LocalName == "tc" && Xml.Parent.Elements(XName.Get("p", DocX.w.NamespaceName)).Count() == 1)
  1108. Xml.Value = string.Empty;
  1109. else
  1110. {
  1111. // Remove this paragraph from the document
  1112. Xml.Remove();
  1113. Xml = null;
  1114. }
  1115. }
  1116. }
  1117. /// <summary>
  1118. /// Gets the text value of this Paragraph.
  1119. /// </summary>
  1120. public string Text
  1121. {
  1122. // Returns the underlying XElement's Value property.
  1123. get
  1124. {
  1125. return HelperFunctions.GetText(Xml);
  1126. }
  1127. }
  1128. //public Picture InsertPicture(Picture picture)
  1129. //{
  1130. // Picture newPicture = picture;
  1131. // newPicture.i = new XElement(picture.i);
  1132. // xml.Add(newPicture.i);
  1133. // pictures.Add(newPicture);
  1134. // return newPicture;
  1135. //}
  1136. /// <summary>
  1137. /// Insert a Picture at the end of this paragraph.
  1138. /// </summary>
  1139. /// <param name="description">A string to describe this Picture.</param>
  1140. /// <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  1141. /// <param name="name">The name of this image.</param>
  1142. /// <returns>A Picture.</returns>
  1143. /// <example>
  1144. /// <code>
  1145. /// // Create a document using a relative filename.
  1146. /// using (DocX document = DocX.Create(@"Test.docx"))
  1147. /// {
  1148. /// // Add a new Paragraph to this document.
  1149. /// Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  1150. ///
  1151. /// // Add an Image to this document.
  1152. /// Novacode.Image img = document.AddImage(@"Image.jpg");
  1153. ///
  1154. /// // Insert pic at the end of Paragraph p.
  1155. /// Picture pic = p.InsertPicture(img.Id, "Photo 31415", "A pie I baked.");
  1156. ///
  1157. /// // Rotate the Picture clockwise by 30 degrees.
  1158. /// pic.Rotation = 30;
  1159. ///
  1160. /// // Resize the Picture.
  1161. /// pic.Width = 400;
  1162. /// pic.Height = 300;
  1163. ///
  1164. /// // Set the shape of this Picture to be a cube.
  1165. /// pic.SetPictureShape(BasicShapes.cube);
  1166. ///
  1167. /// // Flip the Picture Horizontally.
  1168. /// pic.FlipHorizontal = true;
  1169. ///
  1170. /// // Save all changes made to this document.
  1171. /// document.Save();
  1172. /// }// Release this document from memory.
  1173. /// </code>
  1174. /// </example>
  1175. /// Removed to simplify the API.
  1176. //public Picture InsertPicture(string imageID, string name, string description)
  1177. //{
  1178. // Picture p = CreatePicture(Document, imageID, name, description);
  1179. // Xml.Add(p.Xml);
  1180. // return p;
  1181. //}
  1182. // Removed because it confusses the API.
  1183. //public Picture InsertPicture(string imageID)
  1184. //{
  1185. // return InsertPicture(imageID, string.Empty, string.Empty);
  1186. //}
  1187. //public Picture InsertPicture(int index, Picture picture)
  1188. //{
  1189. // Picture p = picture;
  1190. // p.i = new XElement(picture.i);
  1191. // Run run = GetFirstRunEffectedByEdit(index);
  1192. // if (run == null)
  1193. // xml.Add(p.i);
  1194. // else
  1195. // {
  1196. // // Split this run at the point you want to insert
  1197. // XElement[] splitRun = Run.SplitRun(run, index);
  1198. // // Replace the origional run
  1199. // run.Xml.ReplaceWith
  1200. // (
  1201. // splitRun[0],
  1202. // p.i,
  1203. // splitRun[1]
  1204. // );
  1205. // }
  1206. // // Rebuild the run lookup for this paragraph
  1207. // runLookup.Clear();
  1208. // BuildRunLookup(xml);
  1209. // DocX.RenumberIDs(document);
  1210. // return p;
  1211. //}
  1212. /// <summary>
  1213. /// Insert a Picture into this Paragraph at a specified index.
  1214. /// </summary>
  1215. /// <param name="description">A string to describe this Picture.</param>
  1216. /// <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  1217. /// <param name="name">The name of this image.</param>
  1218. /// <param name="index">The index to insert this Picture at.</param>
  1219. /// <returns>A Picture.</returns>
  1220. /// <example>
  1221. /// <code>
  1222. /// // Create a document using a relative filename.
  1223. /// using (DocX document = DocX.Create(@"Test.docx"))
  1224. /// {
  1225. /// // Add a new Paragraph to this document.
  1226. /// Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  1227. ///
  1228. /// // Add an Image to this document.
  1229. /// Novacode.Image img = document.AddImage(@"Image.jpg");
  1230. ///
  1231. /// // Insert pic at the start of Paragraph p.
  1232. /// Picture pic = p.InsertPicture(0, img.Id, "Photo 31415", "A pie I baked.");
  1233. ///
  1234. /// // Rotate the Picture clockwise by 30 degrees.
  1235. /// pic.Rotation = 30;
  1236. ///
  1237. /// // Resize the Picture.
  1238. /// pic.Width = 400;
  1239. /// pic.Height = 300;
  1240. ///
  1241. /// // Set the shape of this Picture to be a cube.
  1242. /// pic.SetPictureShape(BasicShapes.cube);
  1243. ///
  1244. /// // Flip the Picture Horizontally.
  1245. /// pic.FlipHorizontal = true;
  1246. ///
  1247. /// // Save all changes made to this document.
  1248. /// document.Save();
  1249. /// }// Release this document from memory.
  1250. /// </code>
  1251. /// </example>
  1252. /// Removed to simplify API.
  1253. //public Picture InsertPicture(int index, string imageID, string name, string description)
  1254. //{
  1255. // Picture picture = CreatePicture(Document, imageID, name, description);
  1256. // Run run = GetFirstRunEffectedByEdit(index);
  1257. // if (run == null)
  1258. // Xml.Add(picture.Xml);
  1259. // else
  1260. // {
  1261. // // Split this run at the point you want to insert
  1262. // XElement[] splitRun = Run.SplitRun(run, index);
  1263. // // Replace the origional run
  1264. // run.Xml.ReplaceWith
  1265. // (
  1266. // splitRun[0],
  1267. // picture.Xml,
  1268. // splitRun[1]
  1269. // );
  1270. // }
  1271. // HelperFunctions.RenumberIDs(Document);
  1272. // return picture;
  1273. //}
  1274. /// <summary>
  1275. /// Create a new Picture.
  1276. /// </summary>
  1277. /// <param name="id">A unique id that identifies an Image embedded in this document.</param>
  1278. /// <param name="name">The name of this Picture.</param>
  1279. /// <param name="descr">The description of this Picture.</param>
  1280. static internal Picture CreatePicture(DocX document, string id, string name, string descr)
  1281. {
  1282. PackagePart part = document.package.GetPart(document.mainPart.GetRelationship(id).TargetUri);
  1283. int cx, cy;
  1284. using (System.Drawing.Image img = System.Drawing.Image.FromStream(part.GetStream()))
  1285. {
  1286. cx = img.Width * 9526;
  1287. cy = img.Height * 9526;
  1288. }
  1289. XElement e = new XElement(DocX.w + "drawing");
  1290. XElement xml = XElement.Parse
  1291. (string.Format(@"
  1292. <drawing xmlns = ""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  1293. <wp:inline distT=""0"" distB=""0"" distL=""0"" distR=""0"" xmlns:wp=""http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"">
  1294. <wp:extent cx=""{0}"" cy=""{1}"" />
  1295. <wp:effectExtent l=""0"" t=""0"" r=""0"" b=""0"" />
  1296. <wp:docPr id=""1"" name=""{3}"" descr=""{4}"" />
  1297. <wp:cNvGraphicFramePr>
  1298. <a:graphicFrameLocks xmlns:a=""http://schemas.openxmlformats.org/drawingml/2006/main"" noChangeAspect=""1"" />
  1299. </wp:cNvGraphicFramePr>
  1300. <a:graphic xmlns:a=""http://schemas.openxmlformats.org/drawingml/2006/main"">
  1301. <a:graphicData uri=""http://schemas.openxmlformats.org/drawingml/2006/picture"">
  1302. <pic:pic xmlns:pic=""http://schemas.openxmlformats.org/drawingml/2006/picture"">
  1303. <pic:nvPicPr>
  1304. <pic:cNvPr id=""0"" name=""{3}"" />
  1305. <pic:cNvPicPr />
  1306. </pic:nvPicPr>
  1307. <pic:blipFill>
  1308. <a:blip r:embed=""{2}"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships""/>
  1309. <a:stretch>
  1310. <a:fillRect />
  1311. </a:stretch>
  1312. </pic:blipFill>
  1313. <pic:spPr>
  1314. <a:xfrm>
  1315. <a:off x=""0"" y=""0"" />
  1316. <a:ext cx=""{0}"" cy=""{1}"" />
  1317. </a:xfrm>
  1318. <a:prstGeom prst=""rect"">
  1319. <a:avLst />
  1320. </a:prstGeom>
  1321. </pic:spPr>
  1322. </pic:pic>
  1323. </a:graphicData>
  1324. </a:graphic>
  1325. </wp:inline>
  1326. </drawing>
  1327. ", cx, cy, id, name, descr));
  1328. return new Picture(document, xml, new Image(document, document.mainPart.GetRelationship(id)));
  1329. }
  1330. // Removed because it confusses the API.
  1331. //public Picture InsertPicture(int index, string imageID)
  1332. //{
  1333. // return InsertPicture(index, imageID, string.Empty, string.Empty);
  1334. //}
  1335. /// <summary>
  1336. /// Creates an Edit either a ins or a del with the specified content and date
  1337. /// </summary>
  1338. /// <param name="t">The type of this edit (ins or del)</param>
  1339. /// <param name="edit_time">The time stamp to use for this edit</param>
  1340. /// <param name="content">The initial content of this edit</param>
  1341. /// <returns></returns>
  1342. internal static XElement CreateEdit(EditType t, DateTime edit_time, object content)
  1343. {
  1344. if (t == EditType.del)
  1345. {
  1346. foreach (object o in (IEnumerable<XElement>)content)
  1347. {
  1348. if (o is XElement)
  1349. {
  1350. XElement e = (o as XElement);
  1351. IEnumerable<XElement> ts = e.DescendantsAndSelf(XName.Get("t", DocX.w.NamespaceName));
  1352. for(int i = 0; i < ts.Count(); i ++)
  1353. {
  1354. XElement text = ts.ElementAt(i);
  1355. text.ReplaceWith(new XElement(DocX.w + "delText", text.Attributes(), text.Value));
  1356. }
  1357. }
  1358. }
  1359. }
  1360. return
  1361. (
  1362. new XElement(DocX.w + t.ToString(),
  1363. new XAttribute(DocX.w + "id", 0),
  1364. new XAttribute(DocX.w + "author", WindowsIdentity.GetCurrent().Name),
  1365. new XAttribute(DocX.w + "date", edit_time),
  1366. content)
  1367. );
  1368. }
  1369. internal Run GetFirstRunEffectedByEdit(int index, EditType type = EditType.ins)
  1370. {
  1371. int len = HelperFunctions.GetText(Xml).Length;
  1372. // Make sure we are looking within an acceptable index range.
  1373. if (index < 0 || ((type == EditType.ins && index > len) || (type == EditType.del && index >= len)))
  1374. throw new ArgumentOutOfRangeException();
  1375. // Need some memory that can be updated by the recursive search for the XElement to Split.
  1376. int count = 0;
  1377. Run theOne = null;
  1378. GetFirstRunEffectedByEditRecursive(Xml, index, ref count, ref theOne, type);
  1379. return theOne;
  1380. }
  1381. internal void GetFirstRunEffectedByEditRecursive(XElement Xml, int index, ref int count, ref Run theOne, EditType type)
  1382. {
  1383. count += HelperFunctions.GetSize(Xml);
  1384. // If the EditType is deletion then we must return the next blah
  1385. if (count > 0 && ((type == EditType.del && count > index) || (type == EditType.ins && count >= index)))
  1386. {
  1387. // Correct the index
  1388. foreach (XElement e in Xml.ElementsBeforeSelf())
  1389. count -= HelperFunctions.GetSize(e);
  1390. count -= HelperFunctions.GetSize(Xml);
  1391. // We have found the element, now find the run it belongs to.
  1392. while (Xml.Name.LocalName != "r")
  1393. Xml = Xml.Parent;
  1394. theOne = new Run(Document, Xml, count);
  1395. return;
  1396. }
  1397. if (Xml.HasElements)
  1398. foreach (XElement e in Xml.Elements())
  1399. if (theOne == null)
  1400. GetFirstRunEffectedByEditRecursive(e, index, ref count, ref theOne, type);
  1401. }
  1402. /// <!--
  1403. /// Bug found and fixed by krugs525 on August 12 2009.
  1404. /// Use TFS compare to see exact code change.
  1405. /// -->
  1406. static internal int GetElementTextLength(XElement run)
  1407. {
  1408. int count = 0;
  1409. if (run == null)
  1410. return count;
  1411. foreach (var d in run.Descendants())
  1412. {
  1413. switch (d.Name.LocalName)
  1414. {
  1415. case "tab":
  1416. if (d.Parent.Name.LocalName != "tabs")
  1417. goto case "br"; break;
  1418. case "br": count++; break;
  1419. case "t": goto case "delText";
  1420. case "delText": count += d.Value.Length; break;
  1421. default: break;
  1422. }
  1423. }
  1424. return count;
  1425. }
  1426. internal XElement[] SplitEdit(XElement edit, int index, EditType type)
  1427. {
  1428. Run run = GetFirstRunEffectedByEdit(index, type);
  1429. XElement[] splitRun = Run.SplitRun(run, index, type);
  1430. XElement splitLeft = new XElement(edit.Name, edit.Attributes(), run.Xml.ElementsBeforeSelf(), splitRun[0]);
  1431. if (GetElementTextLength(splitLeft) == 0)
  1432. splitLeft = null;
  1433. XElement splitRight = new XElement(edit.Name, edit.Attributes(), splitRun[1], run.Xml.ElementsAfterSelf());
  1434. if (GetElementTextLength(splitRight) == 0)
  1435. splitRight = null;
  1436. return
  1437. (
  1438. new XElement[]
  1439. {
  1440. splitLeft,
  1441. splitRight
  1442. }
  1443. );
  1444. }
  1445. /// <summary>
  1446. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1447. /// </summary>
  1448. /// <example>
  1449. /// <code>
  1450. /// // Create a document using a relative filename.
  1451. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1452. /// {
  1453. /// // Create a text formatting.
  1454. /// Formatting f = new Formatting();
  1455. /// f.FontColor = Color.Red;
  1456. /// f.Size = 30;
  1457. ///
  1458. /// // Iterate through the Paragraphs in this document.
  1459. /// foreach (Paragraph p in document.Paragraphs)
  1460. /// {
  1461. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1462. /// p.InsertText("Start: ", true, f);
  1463. /// }
  1464. ///
  1465. /// // Save all changes made to this document.
  1466. /// document.Save();
  1467. /// }// Release this document from memory.
  1468. /// </code>
  1469. /// </example>
  1470. /// <example>
  1471. /// Inserting tabs using the \t switch.
  1472. /// <code>
  1473. /// // Create a document using a relative filename.
  1474. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1475. /// {
  1476. /// // Create a text formatting.
  1477. /// Formatting f = new Formatting();
  1478. /// f.FontColor = Color.Red;
  1479. /// f.Size = 30;
  1480. ///
  1481. /// // Iterate through the paragraphs in this document.
  1482. /// foreach (Paragraph p in document.Paragraphs)
  1483. /// {
  1484. /// // Insert the string "\tEnd" at the end of every paragraph and flag it as a change.
  1485. /// p.InsertText("\tEnd", true, f);
  1486. /// }
  1487. ///
  1488. /// // Save all changes made to this document.
  1489. /// document.Save();
  1490. /// }// Release this document from memory.
  1491. /// </code>
  1492. /// </example>
  1493. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1494. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1495. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1496. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1497. /// <param name="value">The System.String to insert.</param>
  1498. /// <param name="trackChanges">Flag this insert as a change.</param>
  1499. /// <param name="formatting">The text formatting.</param>
  1500. public void InsertText(string value, bool trackChanges = false, Formatting formatting = null)
  1501. {
  1502. // Default values for optional parameters must be compile time constants.
  1503. // Would have like to write 'public void InsertText(string value, bool trackChanges = false, Formatting formatting = new Formatting())
  1504. if (formatting == null) formatting = new Formatting();
  1505. List<XElement> newRuns = HelperFunctions.FormatInput(value, formatting.Xml);
  1506. Xml.Add(newRuns);
  1507. HelperFunctions.RenumberIDs(Document);
  1508. }
  1509. /// <summary>
  1510. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1511. /// </summary>
  1512. /// <example>
  1513. /// <code>
  1514. /// // Create a document using a relative filename.
  1515. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1516. /// {
  1517. /// // Create a text formatting.
  1518. /// Formatting f = new Formatting();
  1519. /// f.FontColor = Color.Red;
  1520. /// f.Size = 30;
  1521. ///
  1522. /// // Iterate through the Paragraphs in this document.
  1523. /// foreach (Paragraph p in document.Paragraphs)
  1524. /// {
  1525. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1526. /// p.InsertText(0, "Start: ", true, f);
  1527. /// }
  1528. ///
  1529. /// // Save all changes made to this document.
  1530. /// document.Save();
  1531. /// }// Release this document from memory.
  1532. /// </code>
  1533. /// </example>
  1534. /// <example>
  1535. /// Inserting tabs using the \t switch.
  1536. /// <code>
  1537. /// // Create a document using a relative filename.
  1538. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1539. /// {
  1540. /// // Create a text formatting.
  1541. /// Formatting f = new Formatting();
  1542. /// f.FontColor = Color.Red;
  1543. /// f.Size = 30;
  1544. ///
  1545. /// // Iterate through the paragraphs in this document.
  1546. /// foreach (Paragraph p in document.Paragraphs)
  1547. /// {
  1548. /// // Insert the string "\tStart:\t" at the begining of every paragraph and flag it as a change.
  1549. /// p.InsertText(0, "\tStart:\t", true, f);
  1550. /// }
  1551. ///
  1552. /// // Save all changes made to this document.
  1553. /// document.Save();
  1554. /// }// Release this document from memory.
  1555. /// </code>
  1556. /// </example>
  1557. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1558. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  1559. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  1560. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  1561. /// <param name="index">The index position of the insertion.</param>
  1562. /// <param name="value">The System.String to insert.</param>
  1563. /// <param name="trackChanges">Flag this insert as a change.</param>
  1564. /// <param name="formatting">The text formatting.</param>
  1565. public void InsertText(int index, string value, bool trackChanges=false, Formatting formatting = null)
  1566. {
  1567. // Timestamp to mark the start of insert
  1568. DateTime now = DateTime.Now;
  1569. DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  1570. // Get the first run effected by this Insert
  1571. Run run = GetFirstRunEffectedByEdit(index);
  1572. if (run == null)
  1573. {
  1574. object insert;
  1575. if (formatting != null)
  1576. insert = HelperFunctions.FormatInput(value, formatting.Xml);
  1577. else
  1578. insert = HelperFunctions.FormatInput(value, null);
  1579. if (trackChanges)
  1580. insert = CreateEdit(EditType.ins, insert_datetime, insert);
  1581. Xml.Add(insert);
  1582. }
  1583. else
  1584. {
  1585. object newRuns;
  1586. if (formatting != null)
  1587. newRuns = HelperFunctions.FormatInput(value, formatting.Xml);
  1588. else
  1589. newRuns = HelperFunctions.FormatInput(value, run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName)));
  1590. // The parent of this Run
  1591. XElement parentElement = run.Xml.Parent;
  1592. switch (parentElement.Name.LocalName)
  1593. {
  1594. case "ins":
  1595. {
  1596. // The datetime that this ins was created
  1597. DateTime parent_ins_date = DateTime.Parse(parentElement.Attribute(XName.Get("date", DocX.w.NamespaceName)).Value);
  1598. /*
  1599. * Special case: You want to track changes,
  1600. * and the first Run effected by this insert
  1601. * has a datetime stamp equal to now.
  1602. */
  1603. if (trackChanges && parent_ins_date.CompareTo(insert_datetime) == 0)
  1604. {
  1605. /*
  1606. * Inserting into a non edit and this special case, is the same procedure.
  1607. */
  1608. goto default;
  1609. }
  1610. /*
  1611. * If not the special case above,
  1612. * then inserting into an ins or a del, is the same procedure.
  1613. */
  1614. goto case "del";
  1615. }
  1616. case "del":
  1617. {
  1618. object insert = newRuns;
  1619. if (trackChanges)
  1620. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1621. // Split this Edit at the point you want to insert
  1622. XElement[] splitEdit = SplitEdit(parentElement, index, EditType.ins);
  1623. // Replace the origional run
  1624. parentElement.ReplaceWith
  1625. (
  1626. splitEdit[0],
  1627. insert,
  1628. splitEdit[1]
  1629. );
  1630. break;
  1631. }
  1632. default:
  1633. {
  1634. object insert = newRuns;
  1635. if (trackChanges && !parentElement.Name.LocalName.Equals("ins"))
  1636. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1637. // Special case to deal with Page Number elements.
  1638. //if (parentElement.Name.LocalName.Equals("fldSimple"))
  1639. // parentElement.AddBeforeSelf(insert);
  1640. else
  1641. {
  1642. // Split this run at the point you want to insert
  1643. XElement[] splitRun = Run.SplitRun(run, index);
  1644. // Replace the origional run
  1645. run.Xml.ReplaceWith
  1646. (
  1647. splitRun[0],
  1648. insert,
  1649. splitRun[1]
  1650. );
  1651. }
  1652. break;
  1653. }
  1654. }
  1655. }
  1656. HelperFunctions.RenumberIDs(Document);
  1657. }
  1658. /// <summary>
  1659. /// Append text to this Paragraph.
  1660. /// </summary>
  1661. /// <param name="text">The text to append.</param>
  1662. /// <returns>This Paragraph with the new text appened.</returns>
  1663. /// <example>
  1664. /// Add a new Paragraph to this document and then append some text to it.
  1665. /// <code>
  1666. /// // Load a document.
  1667. /// using (DocX document = DocX.Create(@"Test.docx"))
  1668. /// {
  1669. /// // Insert a new Paragraph and Append some text to it.
  1670. /// Paragraph p = document.InsertParagraph().Append("Hello World!!!");
  1671. ///
  1672. /// // Save this document.
  1673. /// document.Save();
  1674. /// }
  1675. /// </code>
  1676. /// </example>
  1677. public Paragraph Append(string text)
  1678. {
  1679. List<XElement> newRuns = HelperFunctions.FormatInput(text, null);
  1680. Xml.Add(newRuns);
  1681. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Reverse().Take(newRuns.Count()).ToList();
  1682. return this;
  1683. }
  1684. /// <summary>
  1685. /// Append a hyperlink to a Paragraph.
  1686. /// </summary>
  1687. /// <param name="h">The hyperlink to append.</param>
  1688. /// <returns>The Paragraph with the hyperlink appended.</returns>
  1689. /// <example>
  1690. /// Creates a Paragraph with some text and a hyperlink.
  1691. /// <code>
  1692. /// // Create a document.
  1693. /// using (DocX document = DocX.Create(@"Test.docx"))
  1694. /// {
  1695. /// // Add a hyperlink to this document.
  1696. /// Hyperlink h = document.AddHyperlink("Google", new Uri("http://www.google.com"));
  1697. ///
  1698. /// // Add a new Paragraph to this document.
  1699. /// Paragraph p = document.InsertParagraph();
  1700. /// p.Append("My favourite search engine is ");
  1701. /// p.AppendHyperlink(h);
  1702. /// p.Append(", I think it's great.");
  1703. ///
  1704. /// // Save all changes made to this document.
  1705. /// document.Save();
  1706. /// }
  1707. /// </code>
  1708. /// </example>
  1709. public Paragraph AppendHyperlink(Hyperlink h)
  1710. {
  1711. // Convert the path of this mainPart to its equilivant rels file path.
  1712. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  1713. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  1714. // Check to see if the rels file exists and create it if not.
  1715. if (!Document.package.PartExists(rels_path))
  1716. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  1717. // Check to see if a rel for this Picture exists, create it if not.
  1718. var Id = GetOrGenerateRel(h);
  1719. Xml.Add(h.Xml);
  1720. Xml.Elements().Last().SetAttributeValue(DocX.r + "id", Id);
  1721. this.runs = Xml.Elements().Last().Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  1722. return this;
  1723. }
  1724. /// <summary>
  1725. /// Add an image to a document, create a custom view of that image (picture) and then insert it into a Paragraph using append.
  1726. /// </summary>
  1727. /// <param name="p">The Picture to append.</param>
  1728. /// <returns>The Paragraph with the Picture now appended.</returns>
  1729. /// <example>
  1730. /// Add an image to a document, create a custom view of that image (picture) and then insert it into a Paragraph using append.
  1731. /// <code>
  1732. /// using (DocX document = DocX.Create("Test.docx"))
  1733. /// {
  1734. /// // Add an image to the document.
  1735. /// Image i = document.AddImage(@"Image.jpg");
  1736. ///
  1737. /// // Create a picture i.e. (A custom view of an image)
  1738. /// Picture p = i.CreatePicture();
  1739. /// p.FlipHorizontal = true;
  1740. /// p.Rotation = 10;
  1741. ///
  1742. /// // Create a new Paragraph.
  1743. /// Paragraph par = document.InsertParagraph();
  1744. ///
  1745. /// // Append content to the Paragraph.
  1746. /// par.Append("Here is a cool picture")
  1747. /// .AppendPicture(p)
  1748. /// .Append(" don't you think so?");
  1749. ///
  1750. /// // Save all changes made to this document.
  1751. /// document.Save();
  1752. /// }
  1753. /// </code>
  1754. /// </example>
  1755. public Paragraph AppendPicture(Picture p)
  1756. {
  1757. // Convert the path of this mainPart to its equilivant rels file path.
  1758. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  1759. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  1760. // Check to see if the rels file exists and create it if not.
  1761. if (!Document.package.PartExists(rels_path))
  1762. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  1763. // Check to see if a rel for this Picture exists, create it if not.
  1764. var Id = GetOrGenerateRel(p);
  1765. // Add the Picture Xml to the end of the Paragragraph Xml.
  1766. Xml.Add(p.Xml);
  1767. // Extract the attribute id from the Pictures Xml.
  1768. XAttribute a_id =
  1769. (
  1770. from e in Xml.Elements().Last().Descendants()
  1771. where e.Name.LocalName.Equals("blip")
  1772. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"))
  1773. ).Single();
  1774. // Set its value to the Pictures relationships id.
  1775. a_id.SetValue(Id);
  1776. // For formatting such as .Bold()
  1777. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Reverse().Take(p.Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Count()).ToList();
  1778. return this;
  1779. }
  1780. /// <summary>
  1781. /// Add an equation to a document.
  1782. /// </summary>
  1783. /// <param name="equation">The Equation to append.</param>
  1784. /// <returns>The Paragraph with the Equation now appended.</returns>
  1785. /// <example>
  1786. /// Add an equation to a document.
  1787. /// <code>
  1788. /// using (DocX document = DocX.Create("Test.docx"))
  1789. /// {
  1790. /// // Add an equation to the document.
  1791. /// document.AddEquation("x=y+z");
  1792. ///
  1793. /// // Save all changes made to this document.
  1794. /// document.Save();
  1795. /// }
  1796. /// </code>
  1797. /// </example>
  1798. public Paragraph AppendEquation(String equation)
  1799. {
  1800. // Create equation element
  1801. XElement oMathPara =
  1802. new XElement
  1803. (
  1804. XName.Get("oMathPara", DocX.m.NamespaceName),
  1805. new XElement
  1806. (
  1807. XName.Get("oMath", DocX.m.NamespaceName),
  1808. new XElement
  1809. (
  1810. XName.Get("r", DocX.w.NamespaceName),
  1811. new Formatting() { FontFamily = new System.Drawing.FontFamily("Cambria Math") }.Xml, // create formatting
  1812. new XElement(XName.Get("t", DocX.m.NamespaceName), equation) // create equation string
  1813. )
  1814. )
  1815. );
  1816. // Add equation element into paragraph xml and update runs collection
  1817. Xml.Add(oMathPara);
  1818. runs = Xml.Elements(XName.Get("oMathPara", DocX.m.NamespaceName)).ToList();
  1819. // Return paragraph with equation
  1820. return this;
  1821. }
  1822. internal string GetOrGenerateRel(Picture p)
  1823. {
  1824. string image_uri_string = p.img.pr.TargetUri.OriginalString;
  1825. // Search for a relationship with a TargetUri that points at this Image.
  1826. var Id =
  1827. (
  1828. from r in mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
  1829. where r.TargetUri.OriginalString == image_uri_string
  1830. select r.Id
  1831. ).SingleOrDefault();
  1832. // If such a relation dosen't exist, create one.
  1833. if (Id == null)
  1834. {
  1835. // Check to see if a relationship for this Picture exists and create it if not.
  1836. PackageRelationship pr = mainPart.CreateRelationship(p.img.pr.TargetUri, TargetMode.Internal, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
  1837. Id = pr.Id;
  1838. }
  1839. return Id;
  1840. }
  1841. internal string GetOrGenerateRel(Hyperlink h)
  1842. {
  1843. string image_uri_string = h.Uri.OriginalString;
  1844. // Search for a relationship with a TargetUri that points at this Image.
  1845. var Id =
  1846. (
  1847. from r in mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
  1848. where r.TargetUri.OriginalString == image_uri_string
  1849. select r.Id
  1850. ).SingleOrDefault();
  1851. // If such a relation dosen't exist, create one.
  1852. if (Id == null)
  1853. {
  1854. // Check to see if a relationship for this Picture exists and create it if not.
  1855. PackageRelationship pr = mainPart.CreateRelationship(h.Uri, TargetMode.External, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
  1856. Id = pr.Id;
  1857. }
  1858. return Id;
  1859. }
  1860. /// <summary>
  1861. /// Insert a Picture into a Paragraph at the given text index.
  1862. /// If not index is provided defaults to 0.
  1863. /// </summary>
  1864. /// <param name="p">The Picture to insert.</param>
  1865. /// <param name="index">The text index to insert at.</param>
  1866. /// <returns>The modified Paragraph.</returns>
  1867. /// <example>
  1868. /// <code>
  1869. ///Load test document.
  1870. ///using (DocX document = DocX.Create("Test.docx"))
  1871. ///{
  1872. /// // Add Headers and Footers into this document.
  1873. /// document.AddHeaders();
  1874. /// document.AddFooters();
  1875. /// document.DifferentFirstPage = true;
  1876. /// document.DifferentOddAndEvenPages = true;
  1877. ///
  1878. /// // Add an Image to this document.
  1879. /// Novacode.Image img = document.AddImage(directory_documents + "purple.png");
  1880. ///
  1881. /// // Create a Picture from this Image.
  1882. /// Picture pic = img.CreatePicture();
  1883. ///
  1884. /// // Main document.
  1885. /// Paragraph p0 = document.InsertParagraph("Hello");
  1886. /// p0.InsertPicture(pic, 3);
  1887. ///
  1888. /// // Header first.
  1889. /// Paragraph p1 = document.Headers.first.InsertParagraph("----");
  1890. /// p1.InsertPicture(pic, 2);
  1891. ///
  1892. /// // Header odd.
  1893. /// Paragraph p2 = document.Headers.odd.InsertParagraph("----");
  1894. /// p2.InsertPicture(pic, 2);
  1895. ///
  1896. /// // Header even.
  1897. /// Paragraph p3 = document.Headers.even.InsertParagraph("----");
  1898. /// p3.InsertPicture(pic, 2);
  1899. ///
  1900. /// // Footer first.
  1901. /// Paragraph p4 = document.Footers.first.InsertParagraph("----");
  1902. /// p4.InsertPicture(pic, 2);
  1903. ///
  1904. /// // Footer odd.
  1905. /// Paragraph p5 = document.Footers.odd.InsertParagraph("----");
  1906. /// p5.InsertPicture(pic, 2);
  1907. ///
  1908. /// // Footer even.
  1909. /// Paragraph p6 = document.Footers.even.InsertParagraph("----");
  1910. /// p6.InsertPicture(pic, 2);
  1911. ///
  1912. /// // Save this document.
  1913. /// document.Save();
  1914. ///}
  1915. /// </code>
  1916. /// </example>
  1917. public Paragraph InsertPicture(Picture p, int index = 0)
  1918. {
  1919. // Convert the path of this mainPart to its equilivant rels file path.
  1920. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  1921. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  1922. // Check to see if the rels file exists and create it if not.
  1923. if (!Document.package.PartExists(rels_path))
  1924. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  1925. // Check to see if a rel for this Picture exists, create it if not.
  1926. var Id = GetOrGenerateRel(p);
  1927. XElement p_xml;
  1928. if (index == 0)
  1929. {
  1930. // Add this hyperlink as the last element.
  1931. Xml.AddFirst(p.Xml);
  1932. // Extract the picture back out of the DOM.
  1933. p_xml = (XElement)Xml.FirstNode;
  1934. }
  1935. else
  1936. {
  1937. // Get the first run effected by this Insert
  1938. Run run = GetFirstRunEffectedByEdit(index);
  1939. if (run == null)
  1940. {
  1941. // Add this picture as the last element.
  1942. Xml.Add(p.Xml);
  1943. // Extract the picture back out of the DOM.
  1944. p_xml = (XElement)Xml.LastNode;
  1945. }
  1946. else
  1947. {
  1948. // Split this run at the point you want to insert
  1949. XElement[] splitRun = Run.SplitRun(run, index);
  1950. // Replace the origional run.
  1951. run.Xml.ReplaceWith
  1952. (
  1953. splitRun[0],
  1954. p.Xml,
  1955. splitRun[1]
  1956. );
  1957. // Get the first run effected by this Insert
  1958. run = GetFirstRunEffectedByEdit(index);
  1959. // The picture has to be the next element, extract it back out of the DOM.
  1960. p_xml = (XElement)run.Xml.NextNode;
  1961. }
  1962. // Extract the attribute id from the Pictures Xml.
  1963. XAttribute a_id =
  1964. (
  1965. from e in p_xml.Descendants()
  1966. where e.Name.LocalName.Equals("blip")
  1967. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"))
  1968. ).Single();
  1969. // Set its value to the Pictures relationships id.
  1970. a_id.SetValue(Id);
  1971. }
  1972. return this;
  1973. }
  1974. /// <summary>
  1975. /// Append text on a new line to this Paragraph.
  1976. /// </summary>
  1977. /// <param name="text">The text to append.</param>
  1978. /// <returns>This Paragraph with the new text appened.</returns>
  1979. /// <example>
  1980. /// Add a new Paragraph to this document and then append a new line with some text to it.
  1981. /// <code>
  1982. /// // Load a document.
  1983. /// using (DocX document = DocX.Create(@"Test.docx"))
  1984. /// {
  1985. /// // Insert a new Paragraph and Append a new line with some text to it.
  1986. /// Paragraph p = document.InsertParagraph().AppendLine("Hello World!!!");
  1987. ///
  1988. /// // Save this document.
  1989. /// document.Save();
  1990. /// }
  1991. /// </code>
  1992. /// </example>
  1993. public Paragraph AppendLine(string text)
  1994. {
  1995. return Append("\n" + text);
  1996. }
  1997. /// <summary>
  1998. /// Append a new line to this Paragraph.
  1999. /// </summary>
  2000. /// <returns>This Paragraph with a new line appeneded.</returns>
  2001. /// <example>
  2002. /// Add a new Paragraph to this document and then append a new line to it.
  2003. /// <code>
  2004. /// // Load a document.
  2005. /// using (DocX document = DocX.Create(@"Test.docx"))
  2006. /// {
  2007. /// // Insert a new Paragraph and Append a new line with some text to it.
  2008. /// Paragraph p = document.InsertParagraph().AppendLine();
  2009. ///
  2010. /// // Save this document.
  2011. /// document.Save();
  2012. /// }
  2013. /// </code>
  2014. /// </example>
  2015. public Paragraph AppendLine()
  2016. {
  2017. return Append("\n");
  2018. }
  2019. internal void ApplyTextFormattingProperty(XName textFormatPropName, string value, object content)
  2020. {
  2021. foreach (XElement run in runs)
  2022. {
  2023. XElement rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2024. if (rPr == null)
  2025. {
  2026. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  2027. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2028. }
  2029. rPr.SetElementValue(textFormatPropName, value);
  2030. XElement last = rPr.Elements().Last();
  2031. last.Add(content);
  2032. }
  2033. }
  2034. /// <summary>
  2035. /// For use with Append() and AppendLine()
  2036. /// </summary>
  2037. /// <returns>This Paragraph with the last appended text bold.</returns>
  2038. /// <example>
  2039. /// Append text to this Paragraph and then make it bold.
  2040. /// <code>
  2041. /// // Create a document.
  2042. /// using (DocX document = DocX.Create(@"Test.docx"))
  2043. /// {
  2044. /// // Insert a new Paragraph.
  2045. /// Paragraph p = document.InsertParagraph();
  2046. ///
  2047. /// p.Append("I am ")
  2048. /// .Append("Bold").Bold()
  2049. /// .Append(" I am not");
  2050. ///
  2051. /// // Save this document.
  2052. /// document.Save();
  2053. /// }// Release this document from memory.
  2054. /// </code>
  2055. /// </example>
  2056. public Paragraph Bold()
  2057. {
  2058. ApplyTextFormattingProperty(XName.Get("b", DocX.w.NamespaceName), string.Empty, null);
  2059. return this;
  2060. }
  2061. /// <summary>
  2062. /// For use with Append() and AppendLine()
  2063. /// </summary>
  2064. /// <returns>This Paragraph with the last appended text italic.</returns>
  2065. /// <example>
  2066. /// Append text to this Paragraph and then make it italic.
  2067. /// <code>
  2068. /// // Create a document.
  2069. /// using (DocX document = DocX.Create(@"Test.docx"))
  2070. /// {
  2071. /// // Insert a new Paragraph.
  2072. /// Paragraph p = document.InsertParagraph();
  2073. ///
  2074. /// p.Append("I am ")
  2075. /// .Append("Italic").Italic()
  2076. /// .Append(" I am not");
  2077. ///
  2078. /// // Save this document.
  2079. /// document.Save();
  2080. /// }// Release this document from memory.
  2081. /// </code>
  2082. /// </example>
  2083. public Paragraph Italic()
  2084. {
  2085. ApplyTextFormattingProperty(XName.Get("i", DocX.w.NamespaceName), string.Empty, null);
  2086. return this;
  2087. }
  2088. /// <summary>
  2089. /// For use with Append() and AppendLine()
  2090. /// </summary>
  2091. /// <param name="c">A color to use on the appended text.</param>
  2092. /// <returns>This Paragraph with the last appended text colored.</returns>
  2093. /// <example>
  2094. /// Append text to this Paragraph and then color it.
  2095. /// <code>
  2096. /// // Create a document.
  2097. /// using (DocX document = DocX.Create(@"Test.docx"))
  2098. /// {
  2099. /// // Insert a new Paragraph.
  2100. /// Paragraph p = document.InsertParagraph();
  2101. ///
  2102. /// p.Append("I am ")
  2103. /// .Append("Blue").Color(Color.Blue)
  2104. /// .Append(" I am not");
  2105. ///
  2106. /// // Save this document.
  2107. /// document.Save();
  2108. /// }// Release this document from memory.
  2109. /// </code>
  2110. /// </example>
  2111. public Paragraph Color(Color c)
  2112. {
  2113. ApplyTextFormattingProperty(XName.Get("color", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), c.ToHex()));
  2114. return this;
  2115. }
  2116. /// <summary>
  2117. /// For use with Append() and AppendLine()
  2118. /// </summary>
  2119. /// <param name="underlineStyle">The underline style to use for the appended text.</param>
  2120. /// <returns>This Paragraph with the last appended text underlined.</returns>
  2121. /// <example>
  2122. /// Append text to this Paragraph and then underline it.
  2123. /// <code>
  2124. /// // Create a document.
  2125. /// using (DocX document = DocX.Create(@"Test.docx"))
  2126. /// {
  2127. /// // Insert a new Paragraph.
  2128. /// Paragraph p = document.InsertParagraph();
  2129. ///
  2130. /// p.Append("I am ")
  2131. /// .Append("Underlined").UnderlineStyle(UnderlineStyle.doubleLine)
  2132. /// .Append(" I am not");
  2133. ///
  2134. /// // Save this document.
  2135. /// document.Save();
  2136. /// }// Release this document from memory.
  2137. /// </code>
  2138. /// </example>
  2139. public Paragraph UnderlineStyle(UnderlineStyle underlineStyle)
  2140. {
  2141. string value;
  2142. switch (underlineStyle)
  2143. {
  2144. case Novacode.UnderlineStyle.none: value = string.Empty; break;
  2145. case Novacode.UnderlineStyle.singleLine: value = "single"; break;
  2146. case Novacode.UnderlineStyle.doubleLine: value = "double"; break;
  2147. default: value = underlineStyle.ToString(); break;
  2148. }
  2149. ApplyTextFormattingProperty(XName.Get("u", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), value));
  2150. return this;
  2151. }
  2152. private Table followingTable;
  2153. ///<summary>
  2154. /// Returns table following the paragraph. Null if the following element isn't table.
  2155. ///</summary>
  2156. public Table FollowingTable
  2157. {
  2158. get
  2159. {
  2160. return followingTable;
  2161. }
  2162. internal set
  2163. {
  2164. followingTable = value;
  2165. }
  2166. }
  2167. /// <summary>
  2168. /// For use with Append() and AppendLine()
  2169. /// </summary>
  2170. /// <param name="fontSize">The font size to use for the appended text.</param>
  2171. /// <returns>This Paragraph with the last appended text resized.</returns>
  2172. /// <example>
  2173. /// Append text to this Paragraph and then resize it.
  2174. /// <code>
  2175. /// // Create a document.
  2176. /// using (DocX document = DocX.Create(@"Test.docx"))
  2177. /// {
  2178. /// // Insert a new Paragraph.
  2179. /// Paragraph p = document.InsertParagraph();
  2180. ///
  2181. /// p.Append("I am ")
  2182. /// .Append("Big").FontSize(20)
  2183. /// .Append(" I am not");
  2184. ///
  2185. /// // Save this document.
  2186. /// document.Save();
  2187. /// }// Release this document from memory.
  2188. /// </code>
  2189. /// </example>
  2190. public Paragraph FontSize(double fontSize)
  2191. {
  2192. if (fontSize - (int)fontSize == 0)
  2193. {
  2194. if (!(fontSize > 0 && fontSize < 1639))
  2195. throw new ArgumentException("Size", "Value must be in the range 0 - 1638");
  2196. }
  2197. else
  2198. throw new ArgumentException("Size", "Value must be either a whole or half number, examples: 32, 32.5");
  2199. ApplyTextFormattingProperty(XName.Get("sz", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  2200. ApplyTextFormattingProperty(XName.Get("szCs", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  2201. return this;
  2202. }
  2203. /// <summary>
  2204. /// For use with Append() and AppendLine()
  2205. /// </summary>
  2206. /// <param name="fontFamily">The font to use for the appended text.</param>
  2207. /// <returns>This Paragraph with the last appended text's font changed.</returns>
  2208. /// <example>
  2209. /// Append text to this Paragraph and then change its font.
  2210. /// <code>
  2211. /// // Create a document.
  2212. /// using (DocX document = DocX.Create(@"Test.docx"))
  2213. /// {
  2214. /// // Insert a new Paragraph.
  2215. /// Paragraph p = document.InsertParagraph();
  2216. ///
  2217. /// p.Append("I am ")
  2218. /// .Append("Times new roman").Font(new FontFamily("Times new roman"))
  2219. /// .Append(" I am not");
  2220. ///
  2221. /// // Save this document.
  2222. /// document.Save();
  2223. /// }// Release this document from memory.
  2224. /// </code>
  2225. /// </example>
  2226. public Paragraph Font(FontFamily fontFamily)
  2227. {
  2228. ApplyTextFormattingProperty
  2229. (
  2230. XName.Get("rFonts", DocX.w.NamespaceName),
  2231. string.Empty,
  2232. new[]
  2233. {
  2234. new XAttribute(XName.Get("ascii", DocX.w.NamespaceName), fontFamily.Name),
  2235. new XAttribute(XName.Get("hAnsi", DocX.w.NamespaceName), fontFamily.Name), // Added by Maurits Elbers to support non-standard characters. See http://docx.codeplex.com/Thread/View.aspx?ThreadId=70097&ANCHOR#Post453865
  2236. new XAttribute(XName.Get("cs", DocX.w.NamespaceName), fontFamily.Name), // Added by Maurits Elbers to support non-standard characters. See http://docx.codeplex.com/Thread/View.aspx?ThreadId=70097&ANCHOR#Post453865
  2237. }
  2238. );
  2239. return this;
  2240. }
  2241. /// <summary>
  2242. /// For use with Append() and AppendLine()
  2243. /// </summary>
  2244. /// <param name="capsStyle">The caps style to apply to the last appended text.</param>
  2245. /// <returns>This Paragraph with the last appended text's caps style changed.</returns>
  2246. /// <example>
  2247. /// Append text to this Paragraph and then set it to full caps.
  2248. /// <code>
  2249. /// // Create a document.
  2250. /// using (DocX document = DocX.Create(@"Test.docx"))
  2251. /// {
  2252. /// // Insert a new Paragraph.
  2253. /// Paragraph p = document.InsertParagraph();
  2254. ///
  2255. /// p.Append("I am ")
  2256. /// .Append("Capitalized").CapsStyle(CapsStyle.caps)
  2257. /// .Append(" I am not");
  2258. ///
  2259. /// // Save this document.
  2260. /// document.Save();
  2261. /// }// Release this document from memory.
  2262. /// </code>
  2263. /// </example>
  2264. public Paragraph CapsStyle(CapsStyle capsStyle)
  2265. {
  2266. switch(capsStyle)
  2267. {
  2268. case Novacode.CapsStyle.none:
  2269. break;
  2270. default:
  2271. {
  2272. ApplyTextFormattingProperty(XName.Get(capsStyle.ToString(), DocX.w.NamespaceName), string.Empty, null);
  2273. break;
  2274. }
  2275. }
  2276. return this;
  2277. }
  2278. /// <summary>
  2279. /// For use with Append() and AppendLine()
  2280. /// </summary>
  2281. /// <param name="script">The script style to apply to the last appended text.</param>
  2282. /// <returns>This Paragraph with the last appended text's script style changed.</returns>
  2283. /// <example>
  2284. /// Append text to this Paragraph and then set it to superscript.
  2285. /// <code>
  2286. /// // Create a document.
  2287. /// using (DocX document = DocX.Create(@"Test.docx"))
  2288. /// {
  2289. /// // Insert a new Paragraph.
  2290. /// Paragraph p = document.InsertParagraph();
  2291. ///
  2292. /// p.Append("I am ")
  2293. /// .Append("superscript").Script(Script.superscript)
  2294. /// .Append(" I am not");
  2295. ///
  2296. /// // Save this document.
  2297. /// document.Save();
  2298. /// }// Release this document from memory.
  2299. /// </code>
  2300. /// </example>
  2301. public Paragraph Script(Script script)
  2302. {
  2303. switch (script)
  2304. {
  2305. case Novacode.Script.none:
  2306. break;
  2307. default:
  2308. {
  2309. ApplyTextFormattingProperty(XName.Get("vertAlign", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), script.ToString()));
  2310. break;
  2311. }
  2312. }
  2313. return this;
  2314. }
  2315. /// <summary>
  2316. /// For use with Append() and AppendLine()
  2317. /// </summary>
  2318. ///<param name="highlight">The highlight to apply to the last appended text.</param>
  2319. /// <returns>This Paragraph with the last appended text highlighted.</returns>
  2320. /// <example>
  2321. /// Append text to this Paragraph and then highlight it.
  2322. /// <code>
  2323. /// // Create a document.
  2324. /// using (DocX document = DocX.Create(@"Test.docx"))
  2325. /// {
  2326. /// // Insert a new Paragraph.
  2327. /// Paragraph p = document.InsertParagraph();
  2328. ///
  2329. /// p.Append("I am ")
  2330. /// .Append("highlighted").Highlight(Highlight.green)
  2331. /// .Append(" I am not");
  2332. ///
  2333. /// // Save this document.
  2334. /// document.Save();
  2335. /// }// Release this document from memory.
  2336. /// </code>
  2337. /// </example>
  2338. public Paragraph Highlight(Highlight highlight)
  2339. {
  2340. switch (highlight)
  2341. {
  2342. case Novacode.Highlight.none:
  2343. break;
  2344. default:
  2345. {
  2346. ApplyTextFormattingProperty(XName.Get("highlight", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), highlight.ToString()));
  2347. break;
  2348. }
  2349. }
  2350. return this;
  2351. }
  2352. /// <summary>
  2353. /// For use with Append() and AppendLine()
  2354. /// </summary>
  2355. /// <param name="misc">The miscellaneous property to set.</param>
  2356. /// <returns>This Paragraph with the last appended text changed by a miscellaneous property.</returns>
  2357. /// <example>
  2358. /// Append text to this Paragraph and then apply a miscellaneous property.
  2359. /// <code>
  2360. /// // Create a document.
  2361. /// using (DocX document = DocX.Create(@"Test.docx"))
  2362. /// {
  2363. /// // Insert a new Paragraph.
  2364. /// Paragraph p = document.InsertParagraph();
  2365. ///
  2366. /// p.Append("I am ")
  2367. /// .Append("outlined").Misc(Misc.outline)
  2368. /// .Append(" I am not");
  2369. ///
  2370. /// // Save this document.
  2371. /// document.Save();
  2372. /// }// Release this document from memory.
  2373. /// </code>
  2374. /// </example>
  2375. public Paragraph Misc(Misc misc)
  2376. {
  2377. switch (misc)
  2378. {
  2379. case Novacode.Misc.none:
  2380. break;
  2381. case Novacode.Misc.outlineShadow:
  2382. {
  2383. ApplyTextFormattingProperty(XName.Get("outline", DocX.w.NamespaceName), string.Empty, null);
  2384. ApplyTextFormattingProperty(XName.Get("shadow", DocX.w.NamespaceName), string.Empty, null);
  2385. break;
  2386. }
  2387. case Novacode.Misc.engrave:
  2388. {
  2389. ApplyTextFormattingProperty(XName.Get("imprint", DocX.w.NamespaceName), string.Empty, null);
  2390. break;
  2391. }
  2392. default:
  2393. {
  2394. ApplyTextFormattingProperty(XName.Get(misc.ToString(), DocX.w.NamespaceName), string.Empty, null);
  2395. break;
  2396. }
  2397. }
  2398. return this;
  2399. }
  2400. /// <summary>
  2401. /// For use with Append() and AppendLine()
  2402. /// </summary>
  2403. /// <param name="strikeThrough">The strike through style to used on the last appended text.</param>
  2404. /// <returns>This Paragraph with the last appended text striked.</returns>
  2405. /// <example>
  2406. /// Append text to this Paragraph and then strike it.
  2407. /// <code>
  2408. /// // Create a document.
  2409. /// using (DocX document = DocX.Create(@"Test.docx"))
  2410. /// {
  2411. /// // Insert a new Paragraph.
  2412. /// Paragraph p = document.InsertParagraph();
  2413. ///
  2414. /// p.Append("I am ")
  2415. /// .Append("striked").StrikeThrough(StrikeThrough.doubleStrike)
  2416. /// .Append(" I am not");
  2417. ///
  2418. /// // Save this document.
  2419. /// document.Save();
  2420. /// }// Release this document from memory.
  2421. /// </code>
  2422. /// </example>
  2423. public Paragraph StrikeThrough(StrikeThrough strikeThrough)
  2424. {
  2425. string value;
  2426. switch (strikeThrough)
  2427. {
  2428. case Novacode.StrikeThrough.strike: value = "strike"; break;
  2429. case Novacode.StrikeThrough.doubleStrike: value = "dstrike"; break;
  2430. default: return this;
  2431. }
  2432. ApplyTextFormattingProperty(XName.Get(value, DocX.w.NamespaceName), string.Empty, null);
  2433. return this;
  2434. }
  2435. /// <summary>
  2436. /// For use with Append() and AppendLine()
  2437. /// </summary>
  2438. /// <param name="underlineColor">The underline color to use, if no underline is set, a single line will be used.</param>
  2439. /// <returns>This Paragraph with the last appended text underlined in a color.</returns>
  2440. /// <example>
  2441. /// Append text to this Paragraph and then underline it using a color.
  2442. /// <code>
  2443. /// // Create a document.
  2444. /// using (DocX document = DocX.Create(@"Test.docx"))
  2445. /// {
  2446. /// // Insert a new Paragraph.
  2447. /// Paragraph p = document.InsertParagraph();
  2448. ///
  2449. /// p.Append("I am ")
  2450. /// .Append("color underlined").UnderlineStyle(UnderlineStyle.dotted).UnderlineColor(Color.Orange)
  2451. /// .Append(" I am not");
  2452. ///
  2453. /// // Save this document.
  2454. /// document.Save();
  2455. /// }// Release this document from memory.
  2456. /// </code>
  2457. /// </example>
  2458. public Paragraph UnderlineColor(Color underlineColor)
  2459. {
  2460. foreach (XElement run in runs)
  2461. {
  2462. XElement rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2463. if (rPr == null)
  2464. {
  2465. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  2466. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2467. }
  2468. XElement u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  2469. if (u == null)
  2470. {
  2471. rPr.SetElementValue(XName.Get("u", DocX.w.NamespaceName), string.Empty);
  2472. u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  2473. u.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), "single");
  2474. }
  2475. u.SetAttributeValue(XName.Get("color", DocX.w.NamespaceName), underlineColor.ToHex());
  2476. }
  2477. return this;
  2478. }
  2479. /// <summary>
  2480. /// For use with Append() and AppendLine()
  2481. /// </summary>
  2482. /// <returns>This Paragraph with the last appended text hidden.</returns>
  2483. /// <example>
  2484. /// Append text to this Paragraph and then hide it.
  2485. /// <code>
  2486. /// // Create a document.
  2487. /// using (DocX document = DocX.Create(@"Test.docx"))
  2488. /// {
  2489. /// // Insert a new Paragraph.
  2490. /// Paragraph p = document.InsertParagraph();
  2491. ///
  2492. /// p.Append("I am ")
  2493. /// .Append("hidden").Hide()
  2494. /// .Append(" I am not");
  2495. ///
  2496. /// // Save this document.
  2497. /// document.Save();
  2498. /// }// Release this document from memory.
  2499. /// </code>
  2500. /// </example>
  2501. public Paragraph Hide()
  2502. {
  2503. ApplyTextFormattingProperty(XName.Get("vanish", DocX.w.NamespaceName), string.Empty, null);
  2504. return this;
  2505. }
  2506. public Paragraph Spacing(double spacing)
  2507. {
  2508. spacing *= 20;
  2509. if (spacing - (int)spacing == 0)
  2510. {
  2511. if (!(spacing > -1585 && spacing < 1585))
  2512. throw new ArgumentException("Spacing", "Value must be in the range: -1584 - 1584");
  2513. }
  2514. else
  2515. throw new ArgumentException("Spacing", "Value must be either a whole or acurate to one decimal, examples: 32, 32.1, 32.2, 32.9");
  2516. ApplyTextFormattingProperty(XName.Get("spacing", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), spacing));
  2517. return this;
  2518. }
  2519. public Paragraph Kerning(int kerning)
  2520. {
  2521. if (!new int?[] { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 }.Contains(kerning))
  2522. 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");
  2523. ApplyTextFormattingProperty(XName.Get("kern", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), kerning * 2));
  2524. return this;
  2525. }
  2526. public Paragraph Position(double position)
  2527. {
  2528. if (!(position > -1585 && position < 1585))
  2529. throw new ArgumentOutOfRangeException("Position", "Value must be in the range -1585 - 1585");
  2530. ApplyTextFormattingProperty(XName.Get("position", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), position * 2));
  2531. return this;
  2532. }
  2533. public Paragraph PercentageScale(int percentageScale)
  2534. {
  2535. if (!(new int?[] { 200, 150, 100, 90, 80, 66, 50, 33 }).Contains(percentageScale))
  2536. throw new ArgumentOutOfRangeException("PercentageScale", "Value must be one of the following: 200, 150, 100, 90, 80, 66, 50 or 33");
  2537. ApplyTextFormattingProperty(XName.Get("w", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), percentageScale));
  2538. return this;
  2539. }
  2540. /// <summary>
  2541. /// Insert a field of type document property, this field will display the custom property cp, at the end of this paragraph.
  2542. /// </summary>
  2543. /// <param name="cp">The custom property to display.</param>
  2544. /// <param name="f">The formatting to use for this text.</param>
  2545. /// <example>
  2546. /// Create, add and display a custom property in a document.
  2547. /// <code>
  2548. /// // Load a document
  2549. /// using (DocX document = DocX.Create(@"Test.docx"))
  2550. /// {
  2551. /// // Create a custom property.
  2552. /// CustomProperty name = new CustomProperty("name", "Cathal Coffey");
  2553. ///
  2554. /// // Add this custom property to this document.
  2555. /// document.AddCustomProperty(name);
  2556. ///
  2557. /// // Create a text formatting.
  2558. /// Formatting f = new Formatting();
  2559. /// f.Bold = true;
  2560. /// f.Size = 14;
  2561. /// f.StrikeThrough = StrickThrough.strike;
  2562. ///
  2563. /// // Insert a new paragraph.
  2564. /// Paragraph p = document.InsertParagraph("Author: ", false, f);
  2565. ///
  2566. /// // Insert a field of type document property to display the custom property name and track this change.
  2567. /// p.InsertDocProperty(name, true, f);
  2568. ///
  2569. /// // Save all changes made to this document.
  2570. /// document.Save();
  2571. /// }// Release this document from memory.
  2572. /// </code>
  2573. /// </example>
  2574. public DocProperty InsertDocProperty(CustomProperty cp, bool trackChanges = false, Formatting f = null)
  2575. {
  2576. XElement e = new XElement
  2577. (
  2578. XName.Get("fldSimple", DocX.w.NamespaceName),
  2579. new XAttribute(XName.Get("instr", DocX.w.NamespaceName), string.Format(@"DOCPROPERTY {0} \* MERGEFORMAT", cp.Name)),
  2580. new XElement(XName.Get("r", DocX.w.NamespaceName),
  2581. new XElement(XName.Get("t", DocX.w.NamespaceName), f.Xml, cp.Value))
  2582. );
  2583. XElement xml = e;
  2584. if (trackChanges)
  2585. {
  2586. DateTime now = DateTime.Now;
  2587. DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  2588. e = CreateEdit(EditType.ins, insert_datetime, e);
  2589. }
  2590. Xml.Add(e);
  2591. return new DocProperty(Document, xml);
  2592. }
  2593. /// <summary>
  2594. /// Removes characters from a Novacode.DocX.Paragraph.
  2595. /// </summary>
  2596. /// <example>
  2597. /// <code>
  2598. /// // Create a document using a relative filename.
  2599. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2600. /// {
  2601. /// // Iterate through the paragraphs
  2602. /// foreach (Paragraph p in document.Paragraphs)
  2603. /// {
  2604. /// // Remove the first two characters from every paragraph
  2605. /// p.RemoveText(0, 2, false);
  2606. /// }
  2607. ///
  2608. /// // Save all changes made to this document.
  2609. /// document.Save();
  2610. /// }// Release this document from memory.
  2611. /// </code>
  2612. /// </example>
  2613. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  2614. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  2615. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2616. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2617. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2618. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2619. /// <param name="index">The position to begin deleting characters.</param>
  2620. /// <param name="count">The number of characters to delete</param>
  2621. /// <param name="trackChanges">Track changes</param>
  2622. public void RemoveText(int index, int count, bool trackChanges = false)
  2623. {
  2624. // Timestamp to mark the start of insert
  2625. DateTime now = DateTime.Now;
  2626. DateTime remove_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  2627. // The number of characters processed so far
  2628. int processed = 0;
  2629. do
  2630. {
  2631. // Get the first run effected by this Remove
  2632. Run run = GetFirstRunEffectedByEdit(index, EditType.del);
  2633. // The parent of this Run
  2634. XElement parentElement = run.Xml.Parent;
  2635. switch (parentElement.Name.LocalName)
  2636. {
  2637. case "ins":
  2638. {
  2639. XElement[] splitEditBefore = SplitEdit(parentElement, index, EditType.del);
  2640. int min = Math.Min(count - processed, run.Xml.ElementsAfterSelf().Sum(e => GetElementTextLength(e)));
  2641. XElement[] splitEditAfter = SplitEdit(parentElement, index + min, EditType.del);
  2642. XElement temp = SplitEdit(splitEditBefore[1], index + min, EditType.del)[0];
  2643. object middle = CreateEdit(EditType.del, remove_datetime, temp.Elements());
  2644. processed += GetElementTextLength(middle as XElement);
  2645. if (!trackChanges)
  2646. middle = null;
  2647. parentElement.ReplaceWith
  2648. (
  2649. splitEditBefore[0],
  2650. middle,
  2651. splitEditAfter[1]
  2652. );
  2653. processed += GetElementTextLength(middle as XElement);
  2654. break;
  2655. }
  2656. case "del":
  2657. {
  2658. if (trackChanges)
  2659. {
  2660. // You cannot delete from a deletion, advance processed to the end of this del
  2661. processed += GetElementTextLength(parentElement);
  2662. }
  2663. else
  2664. goto case "ins";
  2665. break;
  2666. }
  2667. default:
  2668. {
  2669. XElement[] splitRunBefore = Run.SplitRun(run, index, EditType.del);
  2670. int min = Math.Min(index + processed + (count - processed), run.EndIndex);
  2671. XElement[] splitRunAfter = Run.SplitRun(run, min, EditType.del);
  2672. object middle = CreateEdit(EditType.del, remove_datetime, new List<XElement>() { Run.SplitRun(new Run(Document, splitRunBefore[1], run.StartIndex + GetElementTextLength(splitRunBefore[0])), min, EditType.del)[0] });
  2673. processed += GetElementTextLength(middle as XElement);
  2674. if (!trackChanges)
  2675. middle = null;
  2676. run.Xml.ReplaceWith
  2677. (
  2678. splitRunBefore[0],
  2679. middle,
  2680. splitRunAfter[1]
  2681. );
  2682. break;
  2683. }
  2684. }
  2685. // If after this remove the parent element is empty, remove it.
  2686. if (GetElementTextLength(parentElement) == 0)
  2687. {
  2688. if (parentElement.Parent != null && parentElement.Parent.Name.LocalName != "tc")
  2689. {
  2690. // Need to make sure there is no drawing element within the parent element.
  2691. // Picture elements contain no text length but they are still content.
  2692. if (parentElement.Descendants(XName.Get("drawing", DocX.w.NamespaceName)).Count() == 0)
  2693. parentElement.Remove();
  2694. }
  2695. }
  2696. }
  2697. while (processed < count);
  2698. HelperFunctions.RenumberIDs(Document);
  2699. }
  2700. /// <summary>
  2701. /// Removes characters from a Novacode.DocX.Paragraph.
  2702. /// </summary>
  2703. /// <example>
  2704. /// <code>
  2705. /// // Create a document using a relative filename.
  2706. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2707. /// {
  2708. /// // Iterate through the paragraphs
  2709. /// foreach (Paragraph p in document.Paragraphs)
  2710. /// {
  2711. /// // Remove all but the first 2 characters from this Paragraph.
  2712. /// p.RemoveText(2, false);
  2713. /// }
  2714. ///
  2715. /// // Save all changes made to this document.
  2716. /// document.Save();
  2717. /// }// Release this document from memory.
  2718. /// </code>
  2719. /// </example>
  2720. /// <seealso cref="Paragraph.ReplaceText(string, string, bool)"/>
  2721. /// <seealso cref="Paragraph.ReplaceText(string, string, bool, RegexOptions)"/>
  2722. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2723. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2724. /// <param name="index">The position to begin deleting characters.</param>
  2725. /// <param name="trackChanges">Track changes</param>
  2726. public void RemoveText(int index, bool trackChanges = false)
  2727. {
  2728. RemoveText(index, Text.Length - index, trackChanges);
  2729. }
  2730. /// <summary>
  2731. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  2732. /// </summary>
  2733. /// <example>
  2734. /// <code>
  2735. /// // Load a document using a relative filename.
  2736. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  2737. /// {
  2738. /// // The formatting to match.
  2739. /// Formatting matchFormatting = new Formatting();
  2740. /// matchFormatting.Size = 10;
  2741. /// matchFormatting.Italic = true;
  2742. /// matchFormatting.FontFamily = new FontFamily("Times New Roman");
  2743. ///
  2744. /// // The formatting to apply to the inserted text.
  2745. /// Formatting newFormatting = new Formatting();
  2746. /// newFormatting.Size = 22;
  2747. /// newFormatting.UnderlineStyle = UnderlineStyle.dotted;
  2748. /// newFormatting.Bold = true;
  2749. ///
  2750. /// // Iterate through the paragraphs in this document.
  2751. /// foreach (Paragraph p in document.Paragraphs)
  2752. /// {
  2753. /// /*
  2754. /// * Replace all instances of the string "wrong" with the string "right" and ignore case.
  2755. /// * Each inserted instance of "wrong" should use the Formatting newFormatting.
  2756. /// * Only replace an instance of "wrong" if it is Size 10, Italic and Times New Roman.
  2757. /// * SubsetMatch means that the formatting must contain all elements of the match formatting,
  2758. /// * but it can also contain additional formatting for example Color, UnderlineStyle, etc.
  2759. /// * ExactMatch means it must not contain additional formatting.
  2760. /// */
  2761. /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting, matchFormatting, MatchFormattingOptions.SubsetMatch);
  2762. /// }
  2763. ///
  2764. /// // Save all changes made to this document.
  2765. /// document.Save();
  2766. /// }// Release this document from memory.
  2767. /// </code>
  2768. /// </example>
  2769. /// <seealso cref="Paragraph.RemoveText(int, int, bool)"/>
  2770. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  2771. /// <seealso cref="Paragraph.InsertText(string, bool)"/>
  2772. /// <seealso cref="Paragraph.InsertText(int, string, bool)"/>
  2773. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  2774. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  2775. /// <param name="newValue">A System.String to replace all occurances of oldValue.</param>
  2776. /// <param name="oldValue">A System.String to be replaced.</param>
  2777. /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param>
  2778. /// <param name="trackChanges">Track changes</param>
  2779. /// <param name="newFormatting">The formatting to apply to the text being inserted.</param>
  2780. /// <param name="matchFormatting">The formatting that the text must match in order to be replaced.</param>
  2781. /// <param name="fo">How should formatting be matched?</param>
  2782. public void ReplaceText(string oldValue, string newValue, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions fo = MatchFormattingOptions.SubsetMatch)
  2783. {
  2784. MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(oldValue), options);
  2785. // Loop through the matches in reverse order
  2786. foreach (Match m in mc.Cast<Match>().Reverse())
  2787. {
  2788. // Assume the formatting matches until proven otherwise.
  2789. bool formattingMatch = true;
  2790. // Does the user want to match formatting?
  2791. if (matchFormatting != null)
  2792. {
  2793. // The number of characters processed so far
  2794. int processed = 0;
  2795. do
  2796. {
  2797. // Get the next run effected
  2798. Run run = GetFirstRunEffectedByEdit(m.Index + processed);
  2799. // Get this runs properties
  2800. XElement rPr = run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2801. if (rPr == null)
  2802. rPr = new Formatting().Xml;
  2803. /*
  2804. * Make sure that every formatting element in f.xml is also in this run,
  2805. * if this is not true, then their formatting does not match.
  2806. */
  2807. if (!ContainsEveryChildOf(matchFormatting.Xml, rPr, fo))
  2808. {
  2809. formattingMatch = false;
  2810. break;
  2811. }
  2812. // We have processed some characters, so update the counter.
  2813. processed += run.Value.Length;
  2814. } while (processed < m.Length);
  2815. }
  2816. // If the formatting matches, do the replace.
  2817. if(formattingMatch)
  2818. {
  2819. InsertText(m.Index + oldValue.Length, newValue, trackChanges, newFormatting);
  2820. RemoveText(m.Index, m.Length, trackChanges);
  2821. }
  2822. }
  2823. }
  2824. internal bool ContainsEveryChildOf(XElement a, XElement b, MatchFormattingOptions fo)
  2825. {
  2826. foreach (XElement e in a.Elements())
  2827. {
  2828. // If a formatting property has the same name and value, its considered to be equivalent.
  2829. if (!b.Elements(e.Name).Where(bElement => bElement.Value == e.Value).Any())
  2830. return false;
  2831. }
  2832. // If the formatting has to be exact, no additionaly formatting must exist.
  2833. if (fo == MatchFormattingOptions.ExactMatch)
  2834. return a.Elements().Count() == b.Elements().Count();
  2835. return true;
  2836. }
  2837. /// <summary>
  2838. /// Find all instances of a string in this paragraph and return their indexes in a List.
  2839. /// </summary>
  2840. /// <param name="str">The string to find</param>
  2841. /// <returns>A list of indexes.</returns>
  2842. /// <example>
  2843. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  2844. /// <code>
  2845. /// // Load a document
  2846. /// using (DocX document = DocX.Load(@"Test.docx"))
  2847. /// {
  2848. /// // Loop through the paragraphs in this document.
  2849. /// foreach(Paragraph p in document.Paragraphs)
  2850. /// {
  2851. /// // Find all instances of 'go' in this paragraph.
  2852. /// List&lt;int&gt; gos = document.FindAll("go");
  2853. ///
  2854. /// /*
  2855. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  2856. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  2857. /// * in document order, every insert would shift the index of the remaining matches.
  2858. /// */
  2859. /// gos.Reverse();
  2860. /// foreach (int index in gos)
  2861. /// {
  2862. /// p.InsertText(index, "don't ", false);
  2863. /// }
  2864. /// }
  2865. ///
  2866. /// // Save all changes made to this document.
  2867. /// document.Save();
  2868. /// }// Release this document from memory.
  2869. /// </code>
  2870. /// </example>
  2871. public List<int> FindAll(string str)
  2872. {
  2873. return FindAll(str, RegexOptions.None);
  2874. }
  2875. /// <summary>
  2876. /// Find all instances of a string in this paragraph and return their indexes in a List.
  2877. /// </summary>
  2878. /// <param name="str">The string to find</param>
  2879. /// <param name="options">The options to use when finding a string match.</param>
  2880. /// <returns>A list of indexes.</returns>
  2881. /// <example>
  2882. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  2883. /// <code>
  2884. /// // Load a document
  2885. /// using (DocX document = DocX.Load(@"Test.docx"))
  2886. /// {
  2887. /// // Loop through the paragraphs in this document.
  2888. /// foreach(Paragraph p in document.Paragraphs)
  2889. /// {
  2890. /// // Find all instances of 'go' in this paragraph (Ignore case).
  2891. /// List&lt;int&gt; gos = document.FindAll("go", RegexOptions.IgnoreCase);
  2892. ///
  2893. /// /*
  2894. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  2895. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  2896. /// * in document order, every insert would shift the index of the remaining matches.
  2897. /// */
  2898. /// gos.Reverse();
  2899. /// foreach (int index in gos)
  2900. /// {
  2901. /// p.InsertText(index, "don't ", false);
  2902. /// }
  2903. /// }
  2904. ///
  2905. /// // Save all changes made to this document.
  2906. /// document.Save();
  2907. /// }// Release this document from memory.
  2908. /// </code>
  2909. /// </example>
  2910. public List<int> FindAll(string str, RegexOptions options)
  2911. {
  2912. MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(str), options);
  2913. var query =
  2914. (
  2915. from m in mc.Cast<Match>()
  2916. select m.Index
  2917. ).ToList();
  2918. return query;
  2919. }
  2920. /// <summary>
  2921. /// Find all unique instances of the given Regex Pattern
  2922. /// </summary>
  2923. /// <param name="str"></param>
  2924. /// <param name="options"></param>
  2925. /// <returns></returns>
  2926. public List<string> FindAllByPattern(string str, RegexOptions options)
  2927. {
  2928. MatchCollection mc = Regex.Matches(this.Text, str, options);
  2929. var query =
  2930. (
  2931. from m in mc.Cast<Match>()
  2932. select m.Value
  2933. ).ToList();
  2934. return query;
  2935. }
  2936. /// <summary>
  2937. /// Insert a PageNumber place holder into a Paragraph.
  2938. /// This place holder should only be inserted into a Header or Footer Paragraph.
  2939. /// Word will not automatically update this field if it is inserted into a document level Paragraph.
  2940. /// </summary>
  2941. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  2942. /// <param name="index">The text index to insert this PageNumber place holder at.</param>
  2943. /// <example>
  2944. /// <code>
  2945. /// // Create a new document.
  2946. /// using (DocX document = DocX.Create(@"Test.docx"))
  2947. /// {
  2948. /// // Add Headers to the document.
  2949. /// document.AddHeaders();
  2950. ///
  2951. /// // Get the default Header.
  2952. /// Header header = document.Headers.odd;
  2953. ///
  2954. /// // Insert a Paragraph into the Header.
  2955. /// Paragraph p0 = header.InsertParagraph("Page ( of )");
  2956. ///
  2957. /// // Insert place holders for PageNumber and PageCount into the Header.
  2958. /// // Word will replace these with the correct value for each Page.
  2959. /// p0.InsertPageNumber(PageNumberFormat.normal, 6);
  2960. /// p0.InsertPageCount(PageNumberFormat.normal, 11);
  2961. ///
  2962. /// // Save the document.
  2963. /// document.Save();
  2964. /// }
  2965. /// </code>
  2966. /// </example>
  2967. /// <seealso cref="AppendPageCount"/>
  2968. /// <seealso cref="AppendPageNumber"/>
  2969. /// <seealso cref="InsertPageCount"/>
  2970. public void InsertPageNumber(PageNumberFormat pnf, int index = 0)
  2971. {
  2972. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  2973. if (pnf == PageNumberFormat.normal)
  2974. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* MERGEFORMAT "));
  2975. else
  2976. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* ROMAN \* MERGEFORMAT "));
  2977. XElement content = XElement.Parse
  2978. (
  2979. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  2980. <w:rPr>
  2981. <w:noProof />
  2982. </w:rPr>
  2983. <w:t>1</w:t>
  2984. </w:r>"
  2985. );
  2986. fldSimple.Add(content);
  2987. if (index == 0)
  2988. Xml.AddFirst(fldSimple);
  2989. else
  2990. {
  2991. Run r = GetFirstRunEffectedByEdit(index, EditType.ins);
  2992. XElement[] splitEdit = SplitEdit(r.Xml, index, EditType.ins);
  2993. r.Xml.ReplaceWith
  2994. (
  2995. splitEdit[0],
  2996. fldSimple,
  2997. splitEdit[1]
  2998. );
  2999. }
  3000. }
  3001. /// <summary>
  3002. /// Append a PageNumber place holder onto the end of a Paragraph.
  3003. /// </summary>
  3004. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3005. /// <example>
  3006. /// <code>
  3007. /// // Create a new document.
  3008. /// using (DocX document = DocX.Create(@"Test.docx"))
  3009. /// {
  3010. /// // Add Headers to the document.
  3011. /// document.AddHeaders();
  3012. ///
  3013. /// // Get the default Header.
  3014. /// Header header = document.Headers.odd;
  3015. ///
  3016. /// // Insert a Paragraph into the Header.
  3017. /// Paragraph p0 = header.InsertParagraph();
  3018. ///
  3019. /// // Appemd place holders for PageNumber and PageCount into the Header.
  3020. /// // Word will replace these with the correct value for each Page.
  3021. /// p0.Append("Page (");
  3022. /// p0.AppendPageNumber(PageNumberFormat.normal);
  3023. /// p0.Append(" of ");
  3024. /// p0.AppendPageCount(PageNumberFormat.normal);
  3025. /// p0.Append(")");
  3026. ///
  3027. /// // Save the document.
  3028. /// document.Save();
  3029. /// }
  3030. /// </code>
  3031. /// </example>
  3032. /// <seealso cref="AppendPageCount"/>
  3033. /// <seealso cref="InsertPageNumber"/>
  3034. /// <seealso cref="InsertPageCount"/>
  3035. public void AppendPageNumber(PageNumberFormat pnf)
  3036. {
  3037. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3038. if(pnf == PageNumberFormat.normal)
  3039. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* MERGEFORMAT "));
  3040. else
  3041. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* ROMAN \* MERGEFORMAT "));
  3042. XElement content = XElement.Parse
  3043. (
  3044. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3045. <w:rPr>
  3046. <w:noProof />
  3047. </w:rPr>
  3048. <w:t>1</w:t>
  3049. </w:r>"
  3050. );
  3051. fldSimple.Add(content);
  3052. Xml.Add(fldSimple);
  3053. }
  3054. /// <summary>
  3055. /// Insert a PageCount place holder into a Paragraph.
  3056. /// This place holder should only be inserted into a Header or Footer Paragraph.
  3057. /// Word will not automatically update this field if it is inserted into a document level Paragraph.
  3058. /// </summary>
  3059. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3060. /// <param name="index">The text index to insert this PageCount place holder at.</param>
  3061. /// <example>
  3062. /// <code>
  3063. /// // Create a new document.
  3064. /// using (DocX document = DocX.Create(@"Test.docx"))
  3065. /// {
  3066. /// // Add Headers to the document.
  3067. /// document.AddHeaders();
  3068. ///
  3069. /// // Get the default Header.
  3070. /// Header header = document.Headers.odd;
  3071. ///
  3072. /// // Insert a Paragraph into the Header.
  3073. /// Paragraph p0 = header.InsertParagraph("Page ( of )");
  3074. ///
  3075. /// // Insert place holders for PageNumber and PageCount into the Header.
  3076. /// // Word will replace these with the correct value for each Page.
  3077. /// p0.InsertPageNumber(PageNumberFormat.normal, 6);
  3078. /// p0.InsertPageCount(PageNumberFormat.normal, 11);
  3079. ///
  3080. /// // Save the document.
  3081. /// document.Save();
  3082. /// }
  3083. /// </code>
  3084. /// </example>
  3085. /// <seealso cref="AppendPageCount"/>
  3086. /// <seealso cref="AppendPageNumber"/>
  3087. /// <seealso cref="InsertPageNumber"/>
  3088. public void InsertPageCount(PageNumberFormat pnf, int index = 0)
  3089. {
  3090. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3091. if (pnf == PageNumberFormat.normal)
  3092. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* MERGEFORMAT "));
  3093. else
  3094. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* ROMAN \* MERGEFORMAT "));
  3095. XElement content = XElement.Parse
  3096. (
  3097. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3098. <w:rPr>
  3099. <w:noProof />
  3100. </w:rPr>
  3101. <w:t>1</w:t>
  3102. </w:r>"
  3103. );
  3104. fldSimple.Add(content);
  3105. if (index == 0)
  3106. Xml.AddFirst(fldSimple);
  3107. else
  3108. {
  3109. Run r = GetFirstRunEffectedByEdit(index, EditType.ins);
  3110. XElement[] splitEdit = SplitEdit(r.Xml, index, EditType.ins);
  3111. r.Xml.ReplaceWith
  3112. (
  3113. splitEdit[0],
  3114. fldSimple,
  3115. splitEdit[1]
  3116. );
  3117. }
  3118. }
  3119. /// <summary>
  3120. /// Append a PageCount place holder onto the end of a Paragraph.
  3121. /// </summary>
  3122. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3123. /// <example>
  3124. /// <code>
  3125. /// // Create a new document.
  3126. /// using (DocX document = DocX.Create(@"Test.docx"))
  3127. /// {
  3128. /// // Add Headers to the document.
  3129. /// document.AddHeaders();
  3130. ///
  3131. /// // Get the default Header.
  3132. /// Header header = document.Headers.odd;
  3133. ///
  3134. /// // Insert a Paragraph into the Header.
  3135. /// Paragraph p0 = header.InsertParagraph();
  3136. ///
  3137. /// // Appemd place holders for PageNumber and PageCount into the Header.
  3138. /// // Word will replace these with the correct value for each Page.
  3139. /// p0.Append("Page (");
  3140. /// p0.AppendPageNumber(PageNumberFormat.normal);
  3141. /// p0.Append(" of ");
  3142. /// p0.AppendPageCount(PageNumberFormat.normal);
  3143. /// p0.Append(")");
  3144. ///
  3145. /// // Save the document.
  3146. /// document.Save();
  3147. /// }
  3148. /// </code>
  3149. /// </example>
  3150. /// <seealso cref="AppendPageNumber"/>
  3151. /// <seealso cref="InsertPageNumber"/>
  3152. /// <seealso cref="InsertPageCount"/>
  3153. public void AppendPageCount(PageNumberFormat pnf)
  3154. {
  3155. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3156. if (pnf == PageNumberFormat.normal)
  3157. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* MERGEFORMAT "));
  3158. else
  3159. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* ROMAN \* MERGEFORMAT "));
  3160. XElement content = XElement.Parse
  3161. (
  3162. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3163. <w:rPr>
  3164. <w:noProof />
  3165. </w:rPr>
  3166. <w:t>1</w:t>
  3167. </w:r>"
  3168. );
  3169. fldSimple.Add(content);
  3170. Xml.Add(fldSimple);
  3171. }
  3172. }
  3173. public class Run : DocXElement
  3174. {
  3175. // A lookup for the text elements in this paragraph
  3176. Dictionary<int, Text> textLookup = new Dictionary<int, Text>();
  3177. private int startIndex;
  3178. private int endIndex;
  3179. private string text;
  3180. /// <summary>
  3181. /// Gets the start index of this Text (text length before this text)
  3182. /// </summary>
  3183. public int StartIndex { get { return startIndex; } }
  3184. /// <summary>
  3185. /// Gets the end index of this Text (text length before this text + this texts length)
  3186. /// </summary>
  3187. public int EndIndex { get { return endIndex; } }
  3188. /// <summary>
  3189. /// The text value of this text element
  3190. /// </summary>
  3191. internal string Value { set { text = value; } get { return text; } }
  3192. internal Run(DocX document, XElement xml, int startIndex)
  3193. : base(document, xml)
  3194. {
  3195. this.startIndex = startIndex;
  3196. // Get the text elements in this run
  3197. IEnumerable<XElement> texts = xml.Descendants();
  3198. int start = startIndex;
  3199. // Loop through each text in this run
  3200. foreach (XElement te in texts)
  3201. {
  3202. switch (te.Name.LocalName)
  3203. {
  3204. case "tab":
  3205. {
  3206. textLookup.Add(start + 1, new Text(Document, te, start));
  3207. text += "\t";
  3208. start++;
  3209. break;
  3210. }
  3211. case "br":
  3212. {
  3213. textLookup.Add(start + 1, new Text(Document, te, start));
  3214. text += "\n";
  3215. start++;
  3216. break;
  3217. }
  3218. case "t": goto case "delText";
  3219. case "delText":
  3220. {
  3221. // Only add strings which are not empty
  3222. if (te.Value.Length > 0)
  3223. {
  3224. textLookup.Add(start + te.Value.Length, new Text(Document, te, start));
  3225. text += te.Value;
  3226. start += te.Value.Length;
  3227. }
  3228. break;
  3229. }
  3230. default: break;
  3231. }
  3232. }
  3233. endIndex = start;
  3234. }
  3235. static internal XElement[] SplitRun(Run r, int index, EditType type = EditType.ins)
  3236. {
  3237. index = index - r.StartIndex;
  3238. Text t = r.GetFirstTextEffectedByEdit(index, type);
  3239. XElement[] splitText = Text.SplitText(t, index);
  3240. XElement splitLeft = new XElement(r.Xml.Name, r.Xml.Attributes(), r.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName)), t.Xml.ElementsBeforeSelf().Where(n => n.Name.LocalName != "rPr"), splitText[0]);
  3241. if (Paragraph.GetElementTextLength(splitLeft) == 0)
  3242. splitLeft = null;
  3243. XElement splitRight = new XElement(r.Xml.Name, r.Xml.Attributes(), r.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName)), splitText[1], t.Xml.ElementsAfterSelf().Where(n => n.Name.LocalName != "rPr"));
  3244. if (Paragraph.GetElementTextLength(splitRight) == 0)
  3245. splitRight = null;
  3246. return
  3247. (
  3248. new XElement[]
  3249. {
  3250. splitLeft,
  3251. splitRight
  3252. }
  3253. );
  3254. }
  3255. internal Text GetFirstTextEffectedByEdit(int index, EditType type = EditType.ins)
  3256. {
  3257. // Make sure we are looking within an acceptable index range.
  3258. if (index < 0 || index > HelperFunctions.GetText(Xml).Length)
  3259. throw new ArgumentOutOfRangeException();
  3260. // Need some memory that can be updated by the recursive search for the XElement to Split.
  3261. int count = 0;
  3262. Text theOne = null;
  3263. GetFirstTextEffectedByEditRecursive(Xml, index, ref count, ref theOne, type);
  3264. return theOne;
  3265. }
  3266. internal void GetFirstTextEffectedByEditRecursive(XElement Xml, int index, ref int count, ref Text theOne, EditType type = EditType.ins)
  3267. {
  3268. count += HelperFunctions.GetSize(Xml);
  3269. if (count > 0 && ((type == EditType.del && count > index) || (type == EditType.ins && count >= index)))
  3270. {
  3271. theOne = new Text(Document, Xml, count - HelperFunctions.GetSize(Xml));
  3272. return;
  3273. }
  3274. if (Xml.HasElements)
  3275. foreach (XElement e in Xml.Elements())
  3276. if (theOne == null)
  3277. GetFirstTextEffectedByEditRecursive(e, index, ref count, ref theOne);
  3278. }
  3279. }
  3280. internal class Text : DocXElement
  3281. {
  3282. private int startIndex;
  3283. private int endIndex;
  3284. private string text;
  3285. /// <summary>
  3286. /// Gets the start index of this Text (text length before this text)
  3287. /// </summary>
  3288. public int StartIndex { get { return startIndex; } }
  3289. /// <summary>
  3290. /// Gets the end index of this Text (text length before this text + this texts length)
  3291. /// </summary>
  3292. public int EndIndex { get { return endIndex; } }
  3293. /// <summary>
  3294. /// The text value of this text element
  3295. /// </summary>
  3296. public string Value { get { return text; } }
  3297. internal Text(DocX document, XElement xml, int startIndex)
  3298. : base(document, xml)
  3299. {
  3300. this.startIndex = startIndex;
  3301. switch (Xml.Name.LocalName)
  3302. {
  3303. case "t":
  3304. {
  3305. goto case "delText";
  3306. }
  3307. case "delText":
  3308. {
  3309. endIndex = startIndex + xml.Value.Length;
  3310. text = xml.Value;
  3311. break;
  3312. }
  3313. case "br":
  3314. {
  3315. text = "\n";
  3316. endIndex = startIndex + 1;
  3317. break;
  3318. }
  3319. case "tab":
  3320. {
  3321. text = "\t";
  3322. endIndex = startIndex + 1;
  3323. break;
  3324. }
  3325. default:
  3326. {
  3327. break;
  3328. }
  3329. }
  3330. }
  3331. internal static XElement[] SplitText(Text t, int index)
  3332. {
  3333. if (index < t.startIndex || index > t.EndIndex)
  3334. throw new ArgumentOutOfRangeException("index");
  3335. XElement splitLeft = null, splitRight = null;
  3336. if (t.Xml.Name.LocalName == "t" || t.Xml.Name.LocalName == "delText")
  3337. {
  3338. // The origional text element, now containing only the text before the index point.
  3339. splitLeft = new XElement(t.Xml.Name, t.Xml.Attributes(), t.Xml.Value.Substring(0, index - t.startIndex));
  3340. if (splitLeft.Value.Length == 0)
  3341. splitLeft = null;
  3342. else
  3343. PreserveSpace(splitLeft);
  3344. // The origional text element, now containing only the text after the index point.
  3345. splitRight = new XElement(t.Xml.Name, t.Xml.Attributes(), t.Xml.Value.Substring(index - t.startIndex, t.Xml.Value.Length - (index - t.startIndex)));
  3346. if (splitRight.Value.Length == 0)
  3347. splitRight = null;
  3348. else
  3349. PreserveSpace(splitRight);
  3350. }
  3351. else
  3352. {
  3353. if (index == t.EndIndex)
  3354. splitLeft = t.Xml;
  3355. else
  3356. splitRight = t.Xml;
  3357. }
  3358. return
  3359. (
  3360. new XElement[]
  3361. {
  3362. splitLeft,
  3363. splitRight
  3364. }
  3365. );
  3366. }
  3367. /// <summary>
  3368. /// If a text element or delText element, starts or ends with a space,
  3369. /// it must have the attribute space, otherwise it must not have it.
  3370. /// </summary>
  3371. /// <param name="e">The (t or delText) element check</param>
  3372. public static void PreserveSpace(XElement e)
  3373. {
  3374. // PreserveSpace should only be used on (t or delText) elements
  3375. if (!e.Name.Equals(DocX.w + "t") && !e.Name.Equals(DocX.w + "delText"))
  3376. throw new ArgumentException("SplitText can only split elements of type t or delText", "e");
  3377. // Check if this w:t contains a space atribute
  3378. XAttribute space = e.Attributes().Where(a => a.Name.Equals(XNamespace.Xml + "space")).SingleOrDefault();
  3379. // This w:t's text begins or ends with whitespace
  3380. if (e.Value.StartsWith(" ") || e.Value.EndsWith(" "))
  3381. {
  3382. // If this w:t contains no space attribute, add one.
  3383. if (space == null)
  3384. e.Add(new XAttribute(XNamespace.Xml + "space", "preserve"));
  3385. }
  3386. // This w:t's text does not begin or end with a space
  3387. else
  3388. {
  3389. // If this w:r contains a space attribute, remove it.
  3390. if (space != null)
  3391. space.Remove();
  3392. }
  3393. }
  3394. }
  3395. }