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

Paragraph.cs 189KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691
  1. using System;
  2. using System.Linq;
  3. using System.Drawing;
  4. using System.Xml.Linq;
  5. using System.Collections;
  6. using System.IO.Packaging;
  7. using System.Globalization;
  8. using System.Security.Principal;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Text.RegularExpressions;
  12. namespace Novacode
  13. {
  14. /// <summary>
  15. /// Represents a document paragraph.
  16. /// </summary>
  17. public class Paragraph : InsertBeforeOrAfter
  18. {
  19. // The Append family of functions use this List to apply style.
  20. internal List<XElement> runs;
  21. // This paragraphs text alignment
  22. private Alignment alignment;
  23. public ContainerType ParentContainer;
  24. private XElement ParagraphNumberPropertiesBacker { get; set; }
  25. /// <summary>
  26. /// Fetch the paragraph number properties for a list element.
  27. /// </summary>
  28. public XElement ParagraphNumberProperties
  29. {
  30. get
  31. {
  32. return ParagraphNumberPropertiesBacker ?? (ParagraphNumberPropertiesBacker = GetParagraphNumberProperties());
  33. }
  34. }
  35. private XElement GetParagraphNumberProperties()
  36. {
  37. var numPrNode = Xml.Descendants().FirstOrDefault(el => el.Name.LocalName == "numPr");
  38. if (numPrNode != null)
  39. {
  40. var numIdNode = numPrNode.Descendants().First(numId => numId.Name.LocalName == "numId");
  41. var numIdAttribute = numIdNode.Attribute(DocX.w + "val");
  42. if (numIdAttribute != null && numIdAttribute.Value.Equals("0"))
  43. {
  44. return null;
  45. }
  46. }
  47. return numPrNode;
  48. }
  49. private bool? IsListItemBacker { get; set; }
  50. /// <summary>
  51. /// Determine if this paragraph is a list element.
  52. /// </summary>
  53. public bool IsListItem
  54. {
  55. get
  56. {
  57. IsListItemBacker = IsListItemBacker ?? (ParagraphNumberProperties != null);
  58. return (bool)IsListItemBacker;
  59. }
  60. }
  61. private int? IndentLevelBacker { get; set; }
  62. /// <summary>
  63. /// If this element is a list item, get the indentation level of the list item.
  64. /// </summary>
  65. public int? IndentLevel
  66. {
  67. get
  68. {
  69. if (!IsListItem)
  70. {
  71. return null;
  72. }
  73. return IndentLevelBacker ?? (IndentLevelBacker = int.Parse(ParagraphNumberProperties.Descendants().First(el => el.Name.LocalName == "ilvl").GetAttribute(DocX.w + "val")));
  74. }
  75. }
  76. /// <summary>
  77. /// Determine if the list element is a numbered list of bulleted list element
  78. /// </summary>
  79. public ListItemType ListItemType;
  80. internal int startIndex, endIndex;
  81. /// <summary>
  82. /// Returns a list of all Pictures in a Paragraph.
  83. /// </summary>
  84. /// <example>
  85. /// Returns a list of all Pictures in a Paragraph.
  86. /// <code>
  87. /// <![CDATA[
  88. /// // Create a document.
  89. /// using (DocX document = DocX.Load(@"Test.docx"))
  90. /// {
  91. /// // Get the first Paragraph in a document.
  92. /// Paragraph p = document.Paragraphs[0];
  93. ///
  94. /// // Get all of the Pictures in this Paragraph.
  95. /// List<Picture> pictures = p.Pictures;
  96. ///
  97. /// // Save this document.
  98. /// document.Save();
  99. /// }
  100. /// ]]>
  101. /// </code>
  102. /// </example>
  103. public List<Picture> Pictures
  104. {
  105. get
  106. {
  107. List<Picture> pictures =
  108. (
  109. from p in Xml.Descendants()
  110. where (p.Name.LocalName == "drawing")
  111. let id =
  112. (
  113. from e in p.Descendants()
  114. where e.Name.LocalName.Equals("blip")
  115. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
  116. ).SingleOrDefault()
  117. where id != null
  118. let img = new Image(Document, mainPart.GetRelationship(id))
  119. select new Picture(Document, p, img)
  120. ).ToList();
  121. List<Picture> shapes =
  122. (
  123. from p in Xml.Descendants()
  124. where (p.Name.LocalName == "pict")
  125. let id =
  126. (
  127. from e in p.Descendants()
  128. where e.Name.LocalName.Equals("imagedata")
  129. select e.Attribute(XName.Get("id", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")).Value
  130. ).SingleOrDefault()
  131. where id != null
  132. let img = new Image(Document, mainPart.GetRelationship(id))
  133. select new Picture(Document, p, img)
  134. ).ToList();
  135. foreach (Picture p in shapes)
  136. pictures.Add(p);
  137. return pictures;
  138. }
  139. }
  140. /// <summary>
  141. /// Returns a list of Hyperlinks in this Paragraph.
  142. /// </summary>
  143. /// <example>
  144. /// <code>
  145. /// // Create a document.
  146. /// using (DocX document = DocX.Load(@"Test.docx"))
  147. /// {
  148. /// // Get the first Paragraph in this document.
  149. /// Paragraph p = document.Paragraphs[0];
  150. ///
  151. /// // Get all of the hyperlinks in this Paragraph.
  152. /// <![CDATA[ List<Hyperlink> ]]> hyperlinks = paragraph.Hyperlinks;
  153. ///
  154. /// // Change the first hyperlinks text and Uri
  155. /// Hyperlink h0 = hyperlinks[0];
  156. /// h0.Text = "DocX";
  157. /// h0.Uri = new Uri("http://docx.codeplex.com");
  158. ///
  159. /// // Save this document.
  160. /// document.Save();
  161. /// }
  162. /// </code>
  163. /// </example>
  164. public List<Hyperlink> Hyperlinks
  165. {
  166. get
  167. {
  168. List<Hyperlink> hyperlinks = new List<Hyperlink>();
  169. List<XElement> hyperlink_elements =
  170. (
  171. from h in Xml.Descendants()
  172. where (h.Name.LocalName == "hyperlink" || h.Name.LocalName == "instrText")
  173. select h
  174. ).ToList();
  175. foreach (XElement he in hyperlink_elements)
  176. {
  177. if (he.Name.LocalName == "hyperlink")
  178. {
  179. try
  180. {
  181. Hyperlink h = new Hyperlink(Document, mainPart, he);
  182. h.mainPart = mainPart;
  183. hyperlinks.Add(h);
  184. }
  185. catch (Exception) { }
  186. }
  187. else
  188. {
  189. // Find the parent run, no matter how deeply nested we are.
  190. XElement e = he;
  191. while (e.Name.LocalName != "r")
  192. e = e.Parent;
  193. // Take every element until we reach w:fldCharType="end"
  194. List<XElement> hyperlink_runs = new List<XElement>();
  195. foreach (XElement r in e.ElementsAfterSelf(XName.Get("r", DocX.w.NamespaceName)))
  196. {
  197. // Add this run to the list.
  198. hyperlink_runs.Add(r);
  199. XElement fldChar = r.Descendants(XName.Get("fldChar", DocX.w.NamespaceName)).SingleOrDefault<XElement>();
  200. if (fldChar != null)
  201. {
  202. XAttribute fldCharType = fldChar.Attribute(XName.Get("fldCharType", DocX.w.NamespaceName));
  203. if (fldCharType != null && fldCharType.Value.Equals("end", StringComparison.CurrentCultureIgnoreCase))
  204. {
  205. try
  206. {
  207. Hyperlink h = new Hyperlink(Document, he, hyperlink_runs);
  208. h.mainPart = mainPart;
  209. hyperlinks.Add(h);
  210. }
  211. catch (Exception) { }
  212. break;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. return hyperlinks;
  219. }
  220. }
  221. ///<summary>
  222. /// The style name of the paragraph.
  223. ///</summary>
  224. public string StyleName
  225. {
  226. get
  227. {
  228. var element = this.GetOrCreate_pPr();
  229. var styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  230. var attr = styleElement?.Attribute(XName.Get("val", DocX.w.NamespaceName));
  231. if (!string.IsNullOrEmpty(attr?.Value))
  232. {
  233. return attr.Value;
  234. }
  235. return "Normal";
  236. }
  237. set
  238. {
  239. if (string.IsNullOrEmpty(value))
  240. {
  241. value = "Normal";
  242. }
  243. var element = this.GetOrCreate_pPr();
  244. var styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  245. if (styleElement == null)
  246. {
  247. element.Add(new XElement(XName.Get("pStyle", DocX.w.NamespaceName)));
  248. styleElement = element.Element(XName.Get("pStyle", DocX.w.NamespaceName));
  249. }
  250. styleElement.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), value);
  251. }
  252. }
  253. // A collection of field type DocProperty.
  254. private List<DocProperty> docProperties;
  255. internal List<XElement> styles = new List<XElement>();
  256. /// <summary>
  257. /// Returns a list of field type DocProperty in this document.
  258. /// </summary>
  259. public List<DocProperty> DocumentProperties
  260. {
  261. get { return docProperties; }
  262. }
  263. internal Paragraph(DocX document, XElement xml, int startIndex, ContainerType parent = ContainerType.None)
  264. : base(document, xml)
  265. {
  266. ParentContainer = parent;
  267. this.startIndex = startIndex;
  268. this.endIndex = startIndex + GetElementTextLength(xml);
  269. RebuildDocProperties();
  270. // As per Unused code affecting performance (Wiki Link: [discussion:454191]) and coffeycathal suggestion no longer requeried
  271. //#region It's possible that a Paragraph may have pStyle references
  272. //// Check if this Paragraph references any pStyle elements.
  273. //var stylesElements = xml.Descendants(XName.Get("pStyle", DocX.w.NamespaceName));
  274. //// If one or more pStyles are referenced.
  275. //if (stylesElements.Count() > 0)
  276. //{
  277. // Uri style_package_uri = new Uri("/word/styles.xml", UriKind.Relative);
  278. // PackagePart styles_document = document.package.GetPart(style_package_uri);
  279. // using (TextReader tr = new StreamReader(styles_document.GetStream()))
  280. // {
  281. // XDocument style_document = XDocument.Load(tr);
  282. // XElement styles_element = style_document.Element(XName.Get("styles", DocX.w.NamespaceName));
  283. // var styles_element_ids = stylesElements.Select(e => e.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value);
  284. // //foreach(string id in styles_element_ids)
  285. // //{
  286. // // var style =
  287. // // (
  288. // // from d in styles_element.Descendants()
  289. // // let styleId = d.Attribute(XName.Get("styleId", DocX.w.NamespaceName))
  290. // // let type = d.Attribute(XName.Get("type", DocX.w.NamespaceName))
  291. // // where type != null && type.Value == "paragraph" && styleId != null && styleId.Value == id
  292. // // select d
  293. // // ).First();
  294. // // styles.Add(style);
  295. // //}
  296. // }
  297. //}
  298. //#endregion
  299. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  300. }
  301. /// <summary>
  302. /// Insert a new Table before this Paragraph, this Table can be from this document or another document.
  303. /// </summary>
  304. /// <param name="t">The Table t to be inserted.</param>
  305. /// <returns>A new Table inserted before this Paragraph.</returns>
  306. /// <example>
  307. /// Insert a new Table before this Paragraph.
  308. /// <code>
  309. /// // Place holder for a Table.
  310. /// Table t;
  311. ///
  312. /// // Load document a.
  313. /// using (DocX documentA = DocX.Load(@"a.docx"))
  314. /// {
  315. /// // Get the first Table from this document.
  316. /// t = documentA.Tables[0];
  317. /// }
  318. ///
  319. /// // Load document b.
  320. /// using (DocX documentB = DocX.Load(@"b.docx"))
  321. /// {
  322. /// // Get the first Paragraph in document b.
  323. /// Paragraph p2 = documentB.Paragraphs[0];
  324. ///
  325. /// // Insert the Table from document a before this Paragraph.
  326. /// Table newTable = p2.InsertTableBeforeSelf(t);
  327. ///
  328. /// // Save all changes made to document b.
  329. /// documentB.Save();
  330. /// }// Release this document from memory.
  331. /// </code>
  332. /// </example>
  333. public override Table InsertTableBeforeSelf(Table t)
  334. {
  335. t = base.InsertTableBeforeSelf(t);
  336. t.mainPart = mainPart;
  337. return t;
  338. }
  339. private Direction direction;
  340. /// <summary>
  341. /// Gets or Sets the Direction of content in this Paragraph.
  342. /// <example>
  343. /// Create a Paragraph with content that flows right to left. Default is left to right.
  344. /// <code>
  345. /// // Create a new document.
  346. /// using (DocX document = DocX.Create("Test.docx"))
  347. /// {
  348. /// // Create a new Paragraph with the text "Hello World".
  349. /// Paragraph p = document.InsertParagraph("Hello World.");
  350. ///
  351. /// // Make this Paragraph flow right to left. Default is left to right.
  352. /// p.Direction = Direction.RightToLeft;
  353. ///
  354. /// // Save all changes made to this document.
  355. /// document.Save();
  356. /// }
  357. /// </code>
  358. /// </example>
  359. /// </summary>
  360. public Direction Direction
  361. {
  362. get
  363. {
  364. XElement pPr = GetOrCreate_pPr();
  365. XElement bidi = pPr.Element(XName.Get("bidi", DocX.w.NamespaceName));
  366. return bidi == null ? Direction.LeftToRight : Direction.RightToLeft;
  367. }
  368. set
  369. {
  370. direction = value;
  371. XElement pPr = GetOrCreate_pPr();
  372. XElement bidi = pPr.Element(XName.Get("bidi", DocX.w.NamespaceName));
  373. if (direction == Direction.RightToLeft)
  374. {
  375. if (bidi == null)
  376. pPr.Add(new XElement(XName.Get("bidi", DocX.w.NamespaceName)));
  377. }
  378. else
  379. {
  380. bidi?.Remove();
  381. }
  382. }
  383. }
  384. public bool IsKeepWithNext
  385. {
  386. get
  387. {
  388. var pPr = GetOrCreate_pPr();
  389. var keepWithNextE = pPr.Element(XName.Get("keepNext", DocX.w.NamespaceName));
  390. if (keepWithNextE == null)
  391. {
  392. return false;
  393. }
  394. return true;
  395. }
  396. }
  397. /// <summary>
  398. /// This paragraph will be kept on the same page as the next paragraph
  399. /// </summary>
  400. /// <example>
  401. /// Create a Paragraph that will stay on the same page as the paragraph that comes next
  402. /// <code>
  403. /// // Create a new document.
  404. /// using (DocX document = DocX.Create("Test.docx"))
  405. ///
  406. /// {
  407. /// // Create a new Paragraph with the text "Hello World".
  408. /// Paragraph p = document.InsertParagraph("Hello World.");
  409. /// p.KeepWithNext();
  410. /// document.InsertParagraph("Previous paragraph will appear on the same page as this paragraph");
  411. /// // Save all changes made to this document.
  412. /// document.Save();
  413. /// }
  414. /// </code>
  415. /// </example>
  416. /// <param name="keepWithNext"></param>
  417. /// <returns></returns>
  418. public Paragraph KeepWithNext(bool keepWithNext = true)
  419. {
  420. var pPr = GetOrCreate_pPr();
  421. var keepWithNextE = pPr.Element(XName.Get("keepNext", DocX.w.NamespaceName));
  422. if (keepWithNextE == null && keepWithNext)
  423. {
  424. pPr.Add(new XElement(XName.Get("keepNext", DocX.w.NamespaceName)));
  425. }
  426. if (!keepWithNext && keepWithNextE != null)
  427. {
  428. keepWithNextE.Remove();
  429. }
  430. return this;
  431. }
  432. /// <summary>
  433. /// Keep all lines in this paragraph together on a page
  434. /// </summary>
  435. /// <example>
  436. /// Create a Paragraph whose lines will stay together on a single page
  437. /// <code>
  438. /// // Create a new document.
  439. /// using (DocX document = DocX.Create("Test.docx"))
  440. /// {
  441. /// // Create a new Paragraph with the text "Hello World".
  442. /// Paragraph p = document.InsertParagraph("All lines of this paragraph will appear on the same page...\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6...");
  443. /// p.KeepLinesTogether();
  444. /// // Save all changes made to this document.
  445. /// document.Save();
  446. /// }
  447. /// </code>
  448. /// </example>
  449. /// <param name="keepTogether"></param>
  450. /// <returns></returns>
  451. public Paragraph KeepLinesTogether(bool keepTogether = true)
  452. {
  453. var pPr = GetOrCreate_pPr();
  454. var keepLinesE = pPr.Element(XName.Get("keepLines", DocX.w.NamespaceName));
  455. if (keepLinesE == null && keepTogether)
  456. {
  457. pPr.Add(new XElement(XName.Get("keepLines", DocX.w.NamespaceName)));
  458. }
  459. if (!keepTogether)
  460. {
  461. keepLinesE?.Remove();
  462. }
  463. return this;
  464. }
  465. /// <summary>
  466. /// If the pPr element doesent exist it is created, either way it is returned by this function.
  467. /// </summary>
  468. /// <returns>The pPr element for this Paragraph.</returns>
  469. internal XElement GetOrCreate_pPr()
  470. {
  471. // Get the element.
  472. XElement pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  473. // If it dosen't exist, create it.
  474. if (pPr == null)
  475. {
  476. Xml.AddFirst(new XElement(XName.Get("pPr", DocX.w.NamespaceName)));
  477. pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  478. }
  479. // Return the pPr element for this Paragraph.
  480. return pPr;
  481. }
  482. /// <summary>
  483. /// If the ind element doesent exist it is created, either way it is returned by this function.
  484. /// </summary>
  485. /// <returns>The ind element for this Paragraphs pPr.</returns>
  486. internal XElement GetOrCreate_pPr_ind()
  487. {
  488. // Get the element.
  489. XElement pPr = GetOrCreate_pPr();
  490. XElement ind = pPr.Element(XName.Get("ind", DocX.w.NamespaceName));
  491. // If it dosen't exist, create it.
  492. if (ind == null)
  493. {
  494. pPr.Add(new XElement(XName.Get("ind", DocX.w.NamespaceName)));
  495. ind = pPr.Element(XName.Get("ind", DocX.w.NamespaceName));
  496. }
  497. // Return the pPr element for this Paragraph.
  498. return ind;
  499. }
  500. private float indentationFirstLine;
  501. /// <summary>
  502. /// Get or set the indentation of the first line of this Paragraph.
  503. /// </summary>
  504. /// <example>
  505. /// Indent only the first line of a Paragraph.
  506. /// <code>
  507. /// // Create a new document.
  508. /// using (DocX document = DocX.Create("Test.docx"))
  509. /// {
  510. /// // Create a new Paragraph.
  511. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  512. ///
  513. /// // Indent only the first line of the Paragraph.
  514. /// p.IndentationFirstLine = 2.0f;
  515. ///
  516. /// // Save all changes made to this document.
  517. /// document.Save();
  518. /// }
  519. /// </code>
  520. /// </example>
  521. public float IndentationFirstLine
  522. {
  523. get
  524. {
  525. GetOrCreate_pPr();
  526. XElement ind = GetOrCreate_pPr_ind();
  527. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  528. if (firstLine != null)
  529. return float.Parse(firstLine.Value);
  530. return 0.0f;
  531. }
  532. set
  533. {
  534. if (IndentationFirstLine != value)
  535. {
  536. indentationFirstLine = value;
  537. XElement pPr = GetOrCreate_pPr();
  538. XElement ind = GetOrCreate_pPr_ind();
  539. // Paragraph can either be firstLine or hanging (Remove hanging).
  540. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  541. hanging?.Remove();
  542. string indentation = ((indentationFirstLine / 0.1) * 57).ToString();
  543. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  544. if (firstLine != null)
  545. firstLine.Value = indentation;
  546. else
  547. ind.Add(new XAttribute(XName.Get("firstLine", DocX.w.NamespaceName), indentation));
  548. }
  549. }
  550. }
  551. private float indentationHanging;
  552. /// <summary>
  553. /// Get or set the indentation of all but the first line of this Paragraph.
  554. /// </summary>
  555. /// <example>
  556. /// Indent all but the first line of a Paragraph.
  557. /// <code>
  558. /// // Create a new document.
  559. /// using (DocX document = DocX.Create("Test.docx"))
  560. /// {
  561. /// // Create a new Paragraph.
  562. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  563. ///
  564. /// // Indent all but the first line of the Paragraph.
  565. /// p.IndentationHanging = 1.0f;
  566. ///
  567. /// // Save all changes made to this document.
  568. /// document.Save();
  569. /// }
  570. /// </code>
  571. /// </example>
  572. public float IndentationHanging
  573. {
  574. get
  575. {
  576. GetOrCreate_pPr();
  577. XElement ind = GetOrCreate_pPr_ind();
  578. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  579. if (hanging != null)
  580. return float.Parse(hanging.Value) / (57 * 10);
  581. return 0.0f;
  582. }
  583. set
  584. {
  585. if (IndentationHanging != value)
  586. {
  587. indentationHanging = value;
  588. XElement pPr = GetOrCreate_pPr();
  589. XElement ind = GetOrCreate_pPr_ind();
  590. // Paragraph can either be firstLine or hanging (Remove firstLine).
  591. XAttribute firstLine = ind.Attribute(XName.Get("firstLine", DocX.w.NamespaceName));
  592. if (firstLine != null)
  593. firstLine.Remove();
  594. string indentation = ((indentationHanging / 0.1) * 57).ToString();
  595. XAttribute hanging = ind.Attribute(XName.Get("hanging", DocX.w.NamespaceName));
  596. if (hanging != null)
  597. hanging.Value = indentation;
  598. else
  599. ind.Add(new XAttribute(XName.Get("hanging", DocX.w.NamespaceName), indentation));
  600. }
  601. }
  602. }
  603. private float indentationBefore;
  604. /// <summary>
  605. /// Set the before indentation in cm for this Paragraph.
  606. /// </summary>
  607. /// <example>
  608. /// // Indent an entire Paragraph from the left.
  609. /// <code>
  610. /// // Create a new document.
  611. /// using (DocX document = DocX.Create("Test.docx"))
  612. /// {
  613. /// // Create a new Paragraph.
  614. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  615. ///
  616. /// // Indent this entire Paragraph from the left.
  617. /// p.IndentationBefore = 2.0f;
  618. ///
  619. /// // Save all changes made to this document.
  620. /// document.Save();
  621. ///}
  622. /// </code>
  623. /// </example>
  624. public float IndentationBefore
  625. {
  626. get
  627. {
  628. XElement pPr = GetOrCreate_pPr();
  629. XElement ind = GetOrCreate_pPr_ind();
  630. XAttribute left = ind.Attribute(XName.Get("left", DocX.w.NamespaceName));
  631. if (left != null)
  632. return float.Parse(left.Value) / (57 * 10);
  633. return 0.0f;
  634. }
  635. set
  636. {
  637. if (IndentationBefore != value)
  638. {
  639. indentationBefore = value;
  640. XElement pPr = GetOrCreate_pPr();
  641. XElement ind = GetOrCreate_pPr_ind();
  642. string indentation = ((indentationBefore / 0.1) * 57).ToString(CultureInfo.GetCultureInfo("en-GB"));
  643. XAttribute left = ind.Attribute(XName.Get("left", DocX.w.NamespaceName));
  644. if (left != null)
  645. left.Value = indentation;
  646. else
  647. ind.Add(new XAttribute(XName.Get("left", DocX.w.NamespaceName), indentation));
  648. }
  649. }
  650. }
  651. private float indentationAfter;
  652. /// <summary>
  653. /// Set the after indentation in cm for this Paragraph.
  654. /// </summary>
  655. /// <example>
  656. /// // Indent an entire Paragraph from the right.
  657. /// <code>
  658. /// // Create a new document.
  659. /// using (DocX document = DocX.Create("Test.docx"))
  660. /// {
  661. /// // Create a new Paragraph.
  662. /// Paragraph p = document.InsertParagraph("Line 1\nLine 2\nLine 3");
  663. ///
  664. /// // Make the content of this Paragraph flow right to left.
  665. /// p.Direction = Direction.RightToLeft;
  666. ///
  667. /// // Indent this entire Paragraph from the right.
  668. /// p.IndentationAfter = 2.0f;
  669. ///
  670. /// // Save all changes made to this document.
  671. /// document.Save();
  672. /// }
  673. /// </code>
  674. /// </example>
  675. public float IndentationAfter
  676. {
  677. get
  678. {
  679. GetOrCreate_pPr();
  680. XElement ind = GetOrCreate_pPr_ind();
  681. XAttribute right = ind.Attribute(XName.Get("right", DocX.w.NamespaceName));
  682. if (right != null)
  683. return float.Parse(right.Value);
  684. return 0.0f;
  685. }
  686. set
  687. {
  688. if (IndentationAfter != value)
  689. {
  690. indentationAfter = value;
  691. XElement pPr = GetOrCreate_pPr();
  692. XElement ind = GetOrCreate_pPr_ind();
  693. string indentation = ((indentationAfter / 0.1) * 57).ToString();
  694. XAttribute right = ind.Attribute(XName.Get("right", DocX.w.NamespaceName));
  695. if (right != null)
  696. right.Value = indentation;
  697. else
  698. ind.Add(new XAttribute(XName.Get("right", DocX.w.NamespaceName), indentation));
  699. }
  700. }
  701. }
  702. /// <summary>
  703. /// Insert a new Table into this document before this Paragraph.
  704. /// </summary>
  705. /// <param name="rowCount">The number of rows this Table should have.</param>
  706. /// <param name="columnCount">The number of columns this Table should have.</param>
  707. /// <returns>A new Table inserted before this Paragraph.</returns>
  708. /// <example>
  709. /// <code>
  710. /// // Create a new document.
  711. /// using (DocX document = DocX.Create(@"Test.docx"))
  712. /// {
  713. /// //Insert a Paragraph into this document.
  714. /// Paragraph p = document.InsertParagraph("Hello World", false);
  715. ///
  716. /// // Insert a new Table before this Paragraph.
  717. /// Table newTable = p.InsertTableBeforeSelf(2, 2);
  718. /// newTable.Design = TableDesign.LightShadingAccent2;
  719. /// newTable.Alignment = Alignment.center;
  720. ///
  721. /// // Save all changes made to this document.
  722. /// document.Save();
  723. /// }// Release this document from memory.
  724. /// </code>
  725. /// </example>
  726. public override Table InsertTableBeforeSelf(int rowCount, int columnCount)
  727. {
  728. return base.InsertTableBeforeSelf(rowCount, columnCount);
  729. }
  730. /// <summary>
  731. /// Insert a new Table after this Paragraph.
  732. /// </summary>
  733. /// <param name="t">The Table t to be inserted.</param>
  734. /// <returns>A new Table inserted after this Paragraph.</returns>
  735. /// <example>
  736. /// Insert a new Table after this Paragraph.
  737. /// <code>
  738. /// // Place holder for a Table.
  739. /// Table t;
  740. ///
  741. /// // Load document a.
  742. /// using (DocX documentA = DocX.Load(@"a.docx"))
  743. /// {
  744. /// // Get the first Table from this document.
  745. /// t = documentA.Tables[0];
  746. /// }
  747. ///
  748. /// // Load document b.
  749. /// using (DocX documentB = DocX.Load(@"b.docx"))
  750. /// {
  751. /// // Get the first Paragraph in document b.
  752. /// Paragraph p2 = documentB.Paragraphs[0];
  753. ///
  754. /// // Insert the Table from document a after this Paragraph.
  755. /// Table newTable = p2.InsertTableAfterSelf(t);
  756. ///
  757. /// // Save all changes made to document b.
  758. /// documentB.Save();
  759. /// }// Release this document from memory.
  760. /// </code>
  761. /// </example>
  762. public override Table InsertTableAfterSelf(Table t)
  763. {
  764. t = base.InsertTableAfterSelf(t);
  765. t.mainPart = mainPart;
  766. return t;
  767. }
  768. /// <summary>
  769. /// Insert a new Table into this document after this Paragraph.
  770. /// </summary>
  771. /// <param name="rowCount">The number of rows this Table should have.</param>
  772. /// <param name="columnCount">The number of columns this Table should have.</param>
  773. /// <returns>A new Table inserted after this Paragraph.</returns>
  774. /// <example>
  775. /// <code>
  776. /// // Create a new document.
  777. /// using (DocX document = DocX.Create(@"Test.docx"))
  778. /// {
  779. /// //Insert a Paragraph into this document.
  780. /// Paragraph p = document.InsertParagraph("Hello World", false);
  781. ///
  782. /// // Insert a new Table after this Paragraph.
  783. /// Table newTable = p.InsertTableAfterSelf(2, 2);
  784. /// newTable.Design = TableDesign.LightShadingAccent2;
  785. /// newTable.Alignment = Alignment.center;
  786. ///
  787. /// // Save all changes made to this document.
  788. /// document.Save();
  789. /// }// Release this document from memory.
  790. /// </code>
  791. /// </example>
  792. public override Table InsertTableAfterSelf(int rowCount, int columnCount)
  793. {
  794. return base.InsertTableAfterSelf(rowCount, columnCount);
  795. }
  796. /// <summary>
  797. /// Insert a Paragraph before this Paragraph, this Paragraph may have come from the same or another document.
  798. /// </summary>
  799. /// <param name="p">The Paragraph to insert.</param>
  800. /// <returns>The Paragraph now associated with this document.</returns>
  801. /// <example>
  802. /// Take a Paragraph from document a, and insert it into document b before this Paragraph.
  803. /// <code>
  804. /// // Place holder for a Paragraph.
  805. /// Paragraph p;
  806. ///
  807. /// // Load document a.
  808. /// using (DocX documentA = DocX.Load(@"a.docx"))
  809. /// {
  810. /// // Get the first paragraph from this document.
  811. /// p = documentA.Paragraphs[0];
  812. /// }
  813. ///
  814. /// // Load document b.
  815. /// using (DocX documentB = DocX.Load(@"b.docx"))
  816. /// {
  817. /// // Get the first Paragraph in document b.
  818. /// Paragraph p2 = documentB.Paragraphs[0];
  819. ///
  820. /// // Insert the Paragraph from document a before this Paragraph.
  821. /// Paragraph newParagraph = p2.InsertParagraphBeforeSelf(p);
  822. ///
  823. /// // Save all changes made to document b.
  824. /// documentB.Save();
  825. /// }// Release this document from memory.
  826. /// </code>
  827. /// </example>
  828. public override Paragraph InsertParagraphBeforeSelf(Paragraph p)
  829. {
  830. Paragraph p2 = base.InsertParagraphBeforeSelf(p);
  831. p2.PackagePart = mainPart;
  832. return p2;
  833. }
  834. /// <summary>
  835. /// Insert a new Paragraph before this Paragraph.
  836. /// </summary>
  837. /// <param name="text">The initial text for this new Paragraph.</param>
  838. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  839. /// <example>
  840. /// Insert a new paragraph before the first Paragraph in this document.
  841. /// <code>
  842. /// // Create a new document.
  843. /// using (DocX document = DocX.Create(@"Test.docx"))
  844. /// {
  845. /// // Insert a Paragraph into this document.
  846. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  847. ///
  848. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.");
  849. ///
  850. /// // Save all changes made to this new document.
  851. /// document.Save();
  852. /// }// Release this new document form memory.
  853. /// </code>
  854. /// </example>
  855. public override Paragraph InsertParagraphBeforeSelf(string text)
  856. {
  857. Paragraph p = base.InsertParagraphBeforeSelf(text);
  858. p.PackagePart = mainPart;
  859. return p;
  860. }
  861. /// <summary>
  862. /// Insert a new Paragraph before this Paragraph.
  863. /// </summary>
  864. /// <param name="text">The initial text for this new Paragraph.</param>
  865. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  866. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  867. /// <example>
  868. /// Insert a new paragraph before the first Paragraph in this document.
  869. /// <code>
  870. /// // Create a new document.
  871. /// using (DocX document = DocX.Create(@"Test.docx"))
  872. /// {
  873. /// // Insert a Paragraph into this document.
  874. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  875. ///
  876. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false);
  877. ///
  878. /// // Save all changes made to this new document.
  879. /// document.Save();
  880. /// }// Release this new document form memory.
  881. /// </code>
  882. /// </example>
  883. public override Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges)
  884. {
  885. Paragraph p = base.InsertParagraphBeforeSelf(text, trackChanges);
  886. p.PackagePart = mainPart;
  887. return p;
  888. }
  889. /// <summary>
  890. /// Insert a new Paragraph before this Paragraph.
  891. /// </summary>
  892. /// <param name="text">The initial text for this new Paragraph.</param>
  893. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  894. /// <param name="formatting">The formatting to apply to this insertion.</param>
  895. /// <returns>A new Paragraph inserted before this Paragraph.</returns>
  896. /// <example>
  897. /// Insert a new paragraph before the first Paragraph in this document.
  898. /// <code>
  899. /// // Create a new document.
  900. /// using (DocX document = DocX.Create(@"Test.docx"))
  901. /// {
  902. /// // Insert a Paragraph into this document.
  903. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  904. ///
  905. /// Formatting boldFormatting = new Formatting();
  906. /// boldFormatting.Bold = true;
  907. ///
  908. /// p.InsertParagraphBeforeSelf("I was inserted before the next Paragraph.", false, boldFormatting);
  909. ///
  910. /// // Save all changes made to this new document.
  911. /// document.Save();
  912. /// }// Release this new document form memory.
  913. /// </code>
  914. /// </example>
  915. public override Paragraph InsertParagraphBeforeSelf(string text, bool trackChanges, Formatting formatting)
  916. {
  917. Paragraph p = base.InsertParagraphBeforeSelf(text, trackChanges, formatting);
  918. p.PackagePart = mainPart;
  919. return p;
  920. }
  921. /// <summary>
  922. /// Insert a page break before a Paragraph.
  923. /// </summary>
  924. /// <example>
  925. /// Insert 2 Paragraphs into a document with a page break between them.
  926. /// <code>
  927. /// using (DocX document = DocX.Create(@"Test.docx"))
  928. /// {
  929. /// // Insert a new Paragraph.
  930. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  931. ///
  932. /// // Insert a new Paragraph.
  933. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  934. ///
  935. /// // Insert a page break before Paragraph two.
  936. /// p2.InsertPageBreakBeforeSelf();
  937. ///
  938. /// // Save this document.
  939. /// document.Save();
  940. /// }// Release this document from memory.
  941. /// </code>
  942. /// </example>
  943. public override void InsertPageBreakBeforeSelf()
  944. {
  945. base.InsertPageBreakBeforeSelf();
  946. }
  947. /// <summary>
  948. /// Insert a page break after a Paragraph.
  949. /// </summary>
  950. /// <example>
  951. /// Insert 2 Paragraphs into a document with a page break between them.
  952. /// <code>
  953. /// using (DocX document = DocX.Create(@"Test.docx"))
  954. /// {
  955. /// // Insert a new Paragraph.
  956. /// Paragraph p1 = document.InsertParagraph("Paragraph 1", false);
  957. ///
  958. /// // Insert a page break after this Paragraph.
  959. /// p1.InsertPageBreakAfterSelf();
  960. ///
  961. /// // Insert a new Paragraph.
  962. /// Paragraph p2 = document.InsertParagraph("Paragraph 2", false);
  963. ///
  964. /// // Save this document.
  965. /// document.Save();
  966. /// }// Release this document from memory.
  967. /// </code>
  968. /// </example>
  969. public override void InsertPageBreakAfterSelf()
  970. {
  971. base.InsertPageBreakAfterSelf();
  972. }
  973. [Obsolete("Instead use: InsertHyperlink(Hyperlink h, int index)")]
  974. public Paragraph InsertHyperlink(int index, Hyperlink h) { return InsertHyperlink(h, index); }
  975. /// <summary>
  976. /// This function inserts a hyperlink into a Paragraph at a specified character index.
  977. /// </summary>
  978. /// <param name="index">The index to insert at.</param>
  979. /// <param name="h">The hyperlink to insert.</param>
  980. /// <returns>The Paragraph with the Hyperlink inserted at the specified index.</returns>
  981. /// <!--
  982. /// This function was added by Brian Campbell aka chickendelicious on Jun 16 2010
  983. /// Thank you Brian.
  984. /// -->
  985. public Paragraph InsertHyperlink(Hyperlink h, int index = 0)
  986. {
  987. // Convert the path of this mainPart to its equilivant rels file path.
  988. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  989. Uri rels_path = new Uri(String.Format("/word/_rels/{0}.rels", path), UriKind.Relative);
  990. // Check to see if the rels file exists and create it if not.
  991. if (!Document.package.PartExists(rels_path))
  992. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  993. // Check to see if a rel for this Picture exists, create it if not.
  994. var Id = GetOrGenerateRel(h);
  995. XElement h_xml;
  996. if (index == 0)
  997. {
  998. // Add this hyperlink as the last element.
  999. Xml.AddFirst(h.Xml);
  1000. // Extract the picture back out of the DOM.
  1001. h_xml = (XElement)Xml.FirstNode;
  1002. }
  1003. else
  1004. {
  1005. // Get the first run effected by this Insert
  1006. Run run = GetFirstRunEffectedByEdit(index);
  1007. if (run == null)
  1008. {
  1009. // Add this hyperlink as the last element.
  1010. Xml.Add(h.Xml);
  1011. // Extract the picture back out of the DOM.
  1012. h_xml = (XElement)Xml.LastNode;
  1013. }
  1014. else
  1015. {
  1016. // Split this run at the point you want to insert
  1017. XElement[] splitRun = Run.SplitRun(run, index);
  1018. // Replace the origional run.
  1019. run.Xml.ReplaceWith
  1020. (
  1021. splitRun[0],
  1022. h.Xml,
  1023. splitRun[1]
  1024. );
  1025. // Get the first run effected by this Insert
  1026. run = GetFirstRunEffectedByEdit(index);
  1027. // The picture has to be the next element, extract it back out of the DOM.
  1028. h_xml = (XElement)run.Xml.NextNode;
  1029. }
  1030. h_xml.SetAttributeValue(DocX.r + "id", Id);
  1031. }
  1032. return this;
  1033. }
  1034. /// <summary>
  1035. /// Remove the Hyperlink at the provided index. The first hyperlink is at index 0.
  1036. /// Using a negative index or an index greater than the index of the last hyperlink will cause an ArgumentOutOfRangeException() to be thrown.
  1037. /// </summary>
  1038. /// <param name="index">The index of the hyperlink to be removed.</param>
  1039. /// <example>
  1040. /// <code>
  1041. /// // Crete a new document.
  1042. /// using (DocX document = DocX.Create("Test.docx"))
  1043. /// {
  1044. /// // Add a Hyperlink into this document.
  1045. /// Hyperlink h = document.AddHyperlink("link", new Uri("http://www.google.com"));
  1046. ///
  1047. /// // Insert a new Paragraph into the document.
  1048. /// Paragraph p1 = document.InsertParagraph("AC");
  1049. ///
  1050. /// // Insert the hyperlink into this Paragraph.
  1051. /// p1.InsertHyperlink(1, h);
  1052. /// Assert.IsTrue(p1.Text == "AlinkC"); // Make sure the hyperlink was inserted correctly;
  1053. ///
  1054. /// // Remove the hyperlink
  1055. /// p1.RemoveHyperlink(0);
  1056. /// Assert.IsTrue(p1.Text == "AC"); // Make sure the hyperlink was removed correctly;
  1057. /// }
  1058. /// </code>
  1059. /// </example>
  1060. public void RemoveHyperlink(int index)
  1061. {
  1062. // Dosen't make sense to remove a Hyperlink at a negative index.
  1063. if (index < 0)
  1064. throw new ArgumentOutOfRangeException();
  1065. // Need somewhere to store the count.
  1066. int count = 0;
  1067. bool found = false;
  1068. RemoveHyperlinkRecursive(Xml, index, ref count, ref found);
  1069. // If !found then the user tried to remove a hyperlink at an index greater than the last.
  1070. if (!found)
  1071. throw new ArgumentOutOfRangeException();
  1072. }
  1073. internal void RemoveHyperlinkRecursive(XElement xml, int index, ref int count, ref bool found)
  1074. {
  1075. if (xml.Name.LocalName.Equals("hyperlink", StringComparison.CurrentCultureIgnoreCase))
  1076. {
  1077. // This is the hyperlink to be removed.
  1078. if (count == index)
  1079. {
  1080. found = true;
  1081. xml.Remove();
  1082. }
  1083. else
  1084. count++;
  1085. }
  1086. if (xml.HasElements)
  1087. foreach (XElement e in xml.Elements())
  1088. if (!found)
  1089. RemoveHyperlinkRecursive(e, index, ref count, ref found);
  1090. }
  1091. /// <summary>
  1092. /// Insert a Paragraph after this Paragraph, this Paragraph may have come from the same or another document.
  1093. /// </summary>
  1094. /// <param name="p">The Paragraph to insert.</param>
  1095. /// <returns>The Paragraph now associated with this document.</returns>
  1096. /// <example>
  1097. /// Take a Paragraph from document a, and insert it into document b after this Paragraph.
  1098. /// <code>
  1099. /// // Place holder for a Paragraph.
  1100. /// Paragraph p;
  1101. ///
  1102. /// // Load document a.
  1103. /// using (DocX documentA = DocX.Load(@"a.docx"))
  1104. /// {
  1105. /// // Get the first paragraph from this document.
  1106. /// p = documentA.Paragraphs[0];
  1107. /// }
  1108. ///
  1109. /// // Load document b.
  1110. /// using (DocX documentB = DocX.Load(@"b.docx"))
  1111. /// {
  1112. /// // Get the first Paragraph in document b.
  1113. /// Paragraph p2 = documentB.Paragraphs[0];
  1114. ///
  1115. /// // Insert the Paragraph from document a after this Paragraph.
  1116. /// Paragraph newParagraph = p2.InsertParagraphAfterSelf(p);
  1117. ///
  1118. /// // Save all changes made to document b.
  1119. /// documentB.Save();
  1120. /// }// Release this document from memory.
  1121. /// </code>
  1122. /// </example>
  1123. public override Paragraph InsertParagraphAfterSelf(Paragraph p)
  1124. {
  1125. Paragraph p2 = base.InsertParagraphAfterSelf(p);
  1126. p2.PackagePart = mainPart;
  1127. return p2;
  1128. }
  1129. /// <summary>
  1130. /// Insert a new Paragraph after this Paragraph.
  1131. /// </summary>
  1132. /// <param name="text">The initial text for this new Paragraph.</param>
  1133. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  1134. /// <param name="formatting">The formatting to apply to this insertion.</param>
  1135. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  1136. /// <example>
  1137. /// Insert a new paragraph after the first Paragraph in this document.
  1138. /// <code>
  1139. /// // Create a new document.
  1140. /// using (DocX document = DocX.Create(@"Test.docx"))
  1141. /// {
  1142. /// // Insert a Paragraph into this document.
  1143. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  1144. ///
  1145. /// Formatting boldFormatting = new Formatting();
  1146. /// boldFormatting.Bold = true;
  1147. ///
  1148. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false, boldFormatting);
  1149. ///
  1150. /// // Save all changes made to this new document.
  1151. /// document.Save();
  1152. /// }// Release this new document form memory.
  1153. /// </code>
  1154. /// </example>
  1155. public override Paragraph InsertParagraphAfterSelf(string text, bool trackChanges, Formatting formatting)
  1156. {
  1157. Paragraph p = base.InsertParagraphAfterSelf(text, trackChanges, formatting);
  1158. p.PackagePart = mainPart;
  1159. return p;
  1160. }
  1161. /// <summary>
  1162. /// Insert a new Paragraph after this Paragraph.
  1163. /// </summary>
  1164. /// <param name="text">The initial text for this new Paragraph.</param>
  1165. /// <param name="trackChanges">Should this insertion be tracked as a change?</param>
  1166. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  1167. /// <example>
  1168. /// Insert a new paragraph after the first Paragraph in this document.
  1169. /// <code>
  1170. /// // Create a new document.
  1171. /// using (DocX document = DocX.Create(@"Test.docx"))
  1172. /// {
  1173. /// // Insert a Paragraph into this document.
  1174. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  1175. ///
  1176. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.", false);
  1177. ///
  1178. /// // Save all changes made to this new document.
  1179. /// document.Save();
  1180. /// }// Release this new document form memory.
  1181. /// </code>
  1182. /// </example>
  1183. public override Paragraph InsertParagraphAfterSelf(string text, bool trackChanges)
  1184. {
  1185. Paragraph p = base.InsertParagraphAfterSelf(text, trackChanges);
  1186. p.PackagePart = mainPart;
  1187. return p;
  1188. }
  1189. /// <summary>
  1190. /// Insert a new Paragraph after this Paragraph.
  1191. /// </summary>
  1192. /// <param name="text">The initial text for this new Paragraph.</param>
  1193. /// <returns>A new Paragraph inserted after this Paragraph.</returns>
  1194. /// <example>
  1195. /// Insert a new paragraph after the first Paragraph in this document.
  1196. /// <code>
  1197. /// // Create a new document.
  1198. /// using (DocX document = DocX.Create(@"Test.docx"))
  1199. /// {
  1200. /// // Insert a Paragraph into this document.
  1201. /// Paragraph p = document.InsertParagraph("I am a Paragraph", false);
  1202. ///
  1203. /// p.InsertParagraphAfterSelf("I was inserted after the previous Paragraph.");
  1204. ///
  1205. /// // Save all changes made to this new document.
  1206. /// document.Save();
  1207. /// }// Release this new document form memory.
  1208. /// </code>
  1209. /// </example>
  1210. public override Paragraph InsertParagraphAfterSelf(string text)
  1211. {
  1212. Paragraph p = base.InsertParagraphAfterSelf(text);
  1213. p.PackagePart = mainPart;
  1214. return p;
  1215. }
  1216. private void RebuildDocProperties()
  1217. {
  1218. docProperties =
  1219. (
  1220. from xml in Xml.Descendants(XName.Get("fldSimple", DocX.w.NamespaceName))
  1221. select new DocProperty(Document, xml)
  1222. ).ToList();
  1223. }
  1224. /// <summary>
  1225. /// Gets or set this Paragraphs text alignment.
  1226. /// </summary>
  1227. public Alignment Alignment
  1228. {
  1229. get
  1230. {
  1231. XElement pPr = GetOrCreate_pPr();
  1232. XElement jc = pPr.Element(XName.Get("jc", DocX.w.NamespaceName));
  1233. if (jc != null)
  1234. {
  1235. XAttribute a = jc.Attribute(XName.Get("val", DocX.w.NamespaceName));
  1236. switch (a.Value.ToLower())
  1237. {
  1238. case "left": return Novacode.Alignment.left;
  1239. case "right": return Novacode.Alignment.right;
  1240. case "center": return Novacode.Alignment.center;
  1241. case "both": return Novacode.Alignment.both;
  1242. }
  1243. }
  1244. return Novacode.Alignment.left;
  1245. }
  1246. set
  1247. {
  1248. alignment = value;
  1249. XElement pPr = GetOrCreate_pPr();
  1250. XElement jc = pPr.Element(XName.Get("jc", DocX.w.NamespaceName));
  1251. if (alignment != Novacode.Alignment.left)
  1252. {
  1253. if (jc == null)
  1254. pPr.Add(new XElement(XName.Get("jc", DocX.w.NamespaceName), new XAttribute(XName.Get("val", DocX.w.NamespaceName), alignment.ToString())));
  1255. else
  1256. jc.Attribute(XName.Get("val", DocX.w.NamespaceName)).Value = alignment.ToString();
  1257. }
  1258. else
  1259. {
  1260. if (jc != null)
  1261. jc.Remove();
  1262. }
  1263. }
  1264. }
  1265. /// <summary>
  1266. /// Remove this Paragraph from the document.
  1267. /// </summary>
  1268. /// <param name="trackChanges">Should this remove be tracked as a change?</param>
  1269. /// <example>
  1270. /// Remove a Paragraph from a document and track it as a change.
  1271. /// <code>
  1272. /// // Create a document using a relative filename.
  1273. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1274. /// {
  1275. /// // Create and Insert a new Paragraph into this document.
  1276. /// Paragraph p = document.InsertParagraph("Hello", false);
  1277. ///
  1278. /// // Remove the Paragraph and track this as a change.
  1279. /// p.Remove(true);
  1280. ///
  1281. /// // Save all changes made to this document.
  1282. /// document.Save();
  1283. /// }// Release this document from memory.
  1284. /// </code>
  1285. /// </example>
  1286. public void Remove(bool trackChanges)
  1287. {
  1288. if (trackChanges)
  1289. {
  1290. DateTime now = DateTime.Now.ToUniversalTime();
  1291. List<XElement> elements = Xml.Elements().ToList();
  1292. List<XElement> temp = new List<XElement>();
  1293. for (int i = 0; i < elements.Count(); i++)
  1294. {
  1295. XElement e = elements[i];
  1296. if (e.Name.LocalName != "del")
  1297. {
  1298. temp.Add(e);
  1299. e.Remove();
  1300. }
  1301. else
  1302. {
  1303. if (temp.Count() > 0)
  1304. {
  1305. e.AddBeforeSelf(CreateEdit(EditType.del, now, temp.Elements()));
  1306. temp.Clear();
  1307. }
  1308. }
  1309. }
  1310. if (temp.Count() > 0)
  1311. Xml.Add(CreateEdit(EditType.del, now, temp));
  1312. }
  1313. else
  1314. {
  1315. // If this is the only Paragraph in the Cell then we cannot remove it.
  1316. if (Xml.Parent.Name.LocalName == "tc" && Xml.Parent.Elements(XName.Get("p", DocX.w.NamespaceName)).Count() == 1)
  1317. Xml.Value = string.Empty;
  1318. else
  1319. {
  1320. // Remove this paragraph from the document
  1321. Xml.Remove();
  1322. Xml = null;
  1323. }
  1324. }
  1325. }
  1326. /// <summary>
  1327. /// Gets the text value of this Paragraph.
  1328. /// </summary>
  1329. public string Text
  1330. {
  1331. // Returns the underlying XElement's Value property.
  1332. get
  1333. {
  1334. return HelperFunctions.GetText(Xml);
  1335. }
  1336. }
  1337. /// <summary>
  1338. /// Gets the formatted text value of this Paragraph.
  1339. /// </summary>
  1340. public List<FormattedText> MagicText
  1341. {
  1342. // Returns the underlying XElement's Value property.
  1343. get
  1344. {
  1345. try
  1346. {
  1347. return HelperFunctions.GetFormattedText(Xml);
  1348. }
  1349. catch (Exception)
  1350. {
  1351. return null;
  1352. }
  1353. }
  1354. }
  1355. //public Picture InsertPicture(Picture picture)
  1356. //{
  1357. // Picture newPicture = picture;
  1358. // newPicture.i = new XElement(picture.i);
  1359. // xml.Add(newPicture.i);
  1360. // pictures.Add(newPicture);
  1361. // return newPicture;
  1362. //}
  1363. // <summary>
  1364. // Insert a Picture at the end of this paragraph.
  1365. // </summary>
  1366. // <param name="description">A string to describe this Picture.</param>
  1367. // <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  1368. // <param name="name">The name of this image.</param>
  1369. // <returns>A Picture.</returns>
  1370. // <example>
  1371. // <code>
  1372. // // Create a document using a relative filename.
  1373. // using (DocX document = DocX.Create(@"Test.docx"))
  1374. // {
  1375. // // Add a new Paragraph to this document.
  1376. // Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  1377. //
  1378. // // Add an Image to this document.
  1379. // Novacode.Image img = document.AddImage(@"Image.jpg");
  1380. //
  1381. // // Insert pic at the end of Paragraph p.
  1382. // Picture pic = p.InsertPicture(img.Id, "Photo 31415", "A pie I baked.");
  1383. //
  1384. // // Rotate the Picture clockwise by 30 degrees.
  1385. // pic.Rotation = 30;
  1386. //
  1387. // // Resize the Picture.
  1388. // pic.Width = 400;
  1389. // pic.Height = 300;
  1390. //
  1391. // // Set the shape of this Picture to be a cube.
  1392. // pic.SetPictureShape(BasicShapes.cube);
  1393. //
  1394. // // Flip the Picture Horizontally.
  1395. // pic.FlipHorizontal = true;
  1396. //
  1397. // // Save all changes made to this document.
  1398. // document.Save();
  1399. // }// Release this document from memory.
  1400. // </code>
  1401. // </example>
  1402. // Removed to simplify the API.
  1403. //public Picture InsertPicture(string imageID, string name, string description)
  1404. //{
  1405. // Picture p = CreatePicture(Document, imageID, name, description);
  1406. // Xml.Add(p.Xml);
  1407. // return p;
  1408. //}
  1409. // Removed because it confusses the API.
  1410. //public Picture InsertPicture(string imageID)
  1411. //{
  1412. // return InsertPicture(imageID, string.Empty, string.Empty);
  1413. //}
  1414. //public Picture InsertPicture(int index, Picture picture)
  1415. //{
  1416. // Picture p = picture;
  1417. // p.i = new XElement(picture.i);
  1418. // Run run = GetFirstRunEffectedByEdit(index);
  1419. // if (run == null)
  1420. // xml.Add(p.i);
  1421. // else
  1422. // {
  1423. // // Split this run at the point you want to insert
  1424. // XElement[] splitRun = Run.SplitRun(run, index);
  1425. // // Replace the origional run
  1426. // run.Xml.ReplaceWith
  1427. // (
  1428. // splitRun[0],
  1429. // p.i,
  1430. // splitRun[1]
  1431. // );
  1432. // }
  1433. // // Rebuild the run lookup for this paragraph
  1434. // runLookup.Clear();
  1435. // BuildRunLookup(xml);
  1436. // DocX.RenumberIDs(document);
  1437. // return p;
  1438. //}
  1439. // <summary>
  1440. // Insert a Picture into this Paragraph at a specified index.
  1441. // </summary>
  1442. // <param name="description">A string to describe this Picture.</param>
  1443. // <param name="imageID">The unique id that identifies the Image this Picture represents.</param>
  1444. // <param name="name">The name of this image.</param>
  1445. // <param name="index">The index to insert this Picture at.</param>
  1446. // <returns>A Picture.</returns>
  1447. // <example>
  1448. // <code>
  1449. // // Create a document using a relative filename.
  1450. // using (DocX document = DocX.Create(@"Test.docx"))
  1451. // {
  1452. // // Add a new Paragraph to this document.
  1453. // Paragraph p = document.InsertParagraph("Here is Picture 1", false);
  1454. //
  1455. // // Add an Image to this document.
  1456. // Novacode.Image img = document.AddImage(@"Image.jpg");
  1457. //
  1458. // // Insert pic at the start of Paragraph p.
  1459. // Picture pic = p.InsertPicture(0, img.Id, "Photo 31415", "A pie I baked.");
  1460. //
  1461. // // Rotate the Picture clockwise by 30 degrees.
  1462. // pic.Rotation = 30;
  1463. //
  1464. // // Resize the Picture.
  1465. // pic.Width = 400;
  1466. // pic.Height = 300;
  1467. //
  1468. // // Set the shape of this Picture to be a cube.
  1469. // pic.SetPictureShape(BasicShapes.cube);
  1470. //
  1471. // // Flip the Picture Horizontally.
  1472. // pic.FlipHorizontal = true;
  1473. //
  1474. // // Save all changes made to this document.
  1475. // document.Save();
  1476. // }// Release this document from memory.
  1477. // </code>
  1478. // </example>
  1479. // Removed to simplify API.
  1480. //public Picture InsertPicture(int index, string imageID, string name, string description)
  1481. //{
  1482. // Picture picture = CreatePicture(Document, imageID, name, description);
  1483. // Run run = GetFirstRunEffectedByEdit(index);
  1484. // if (run == null)
  1485. // Xml.Add(picture.Xml);
  1486. // else
  1487. // {
  1488. // // Split this run at the point you want to insert
  1489. // XElement[] splitRun = Run.SplitRun(run, index);
  1490. // // Replace the origional run
  1491. // run.Xml.ReplaceWith
  1492. // (
  1493. // splitRun[0],
  1494. // picture.Xml,
  1495. // splitRun[1]
  1496. // );
  1497. // }
  1498. // HelperFunctions.RenumberIDs(Document);
  1499. // return picture;
  1500. //}
  1501. /// <summary>
  1502. /// Create a new Picture.
  1503. /// </summary>
  1504. /// <param name="document"></param>
  1505. /// <param name="id">A unique id that identifies an Image embedded in this document.</param>
  1506. /// <param name="name">The name of this Picture.</param>
  1507. /// <param name="descr">The description of this Picture.</param>
  1508. static internal Picture CreatePicture(DocX document, string id, string name, string descr)
  1509. {
  1510. PackagePart part = document.package.GetPart(document.mainPart.GetRelationship(id).TargetUri);
  1511. long newDocPrId = document.GetNextFreeDocPrId();
  1512. int cx, cy;
  1513. using (PackagePartStream packagePartStream = new PackagePartStream(part.GetStream()))
  1514. {
  1515. using (System.Drawing.Image img = System.Drawing.Image.FromStream(packagePartStream, useEmbeddedColorManagement: false, validateImageData: false))
  1516. {
  1517. cx = img.Width*9526;
  1518. cy = img.Height*9526;
  1519. }
  1520. }
  1521. XElement xml = XElement.Parse
  1522. (string.Format(@"<w:r xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  1523. <w:drawing xmlns = ""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  1524. <wp:inline distT=""0"" distB=""0"" distL=""0"" distR=""0"" xmlns:wp=""http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"">
  1525. <wp:extent cx=""{0}"" cy=""{1}"" />
  1526. <wp:effectExtent l=""0"" t=""0"" r=""0"" b=""0"" />
  1527. <wp:docPr id=""{5}"" name=""{3}"" descr=""{4}"" />
  1528. <wp:cNvGraphicFramePr>
  1529. <a:graphicFrameLocks xmlns:a=""http://schemas.openxmlformats.org/drawingml/2006/main"" noChangeAspect=""1"" />
  1530. </wp:cNvGraphicFramePr>
  1531. <a:graphic xmlns:a=""http://schemas.openxmlformats.org/drawingml/2006/main"">
  1532. <a:graphicData uri=""http://schemas.openxmlformats.org/drawingml/2006/picture"">
  1533. <pic:pic xmlns:pic=""http://schemas.openxmlformats.org/drawingml/2006/picture"">
  1534. <pic:nvPicPr>
  1535. <pic:cNvPr id=""0"" name=""{3}"" />
  1536. <pic:cNvPicPr />
  1537. </pic:nvPicPr>
  1538. <pic:blipFill>
  1539. <a:blip r:embed=""{2}"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships""/>
  1540. <a:stretch>
  1541. <a:fillRect />
  1542. </a:stretch>
  1543. </pic:blipFill>
  1544. <pic:spPr>
  1545. <a:xfrm>
  1546. <a:off x=""0"" y=""0"" />
  1547. <a:ext cx=""{0}"" cy=""{1}"" />
  1548. </a:xfrm>
  1549. <a:prstGeom prst=""rect"">
  1550. <a:avLst />
  1551. </a:prstGeom>
  1552. </pic:spPr>
  1553. </pic:pic>
  1554. </a:graphicData>
  1555. </a:graphic>
  1556. </wp:inline>
  1557. </w:drawing></w:r>
  1558. ", cx, cy, id, name, descr, newDocPrId.ToString()));
  1559. return new Picture(document, xml, new Image(document, document.mainPart.GetRelationship(id)));
  1560. }
  1561. // Removed because it confusses the API.
  1562. //public Picture InsertPicture(int index, string imageID)
  1563. //{
  1564. // return InsertPicture(index, imageID, string.Empty, string.Empty);
  1565. //}
  1566. /// <summary>
  1567. /// Creates an Edit either a ins or a del with the specified content and date
  1568. /// </summary>
  1569. /// <param name="t">The type of this edit (ins or del)</param>
  1570. /// <param name="edit_time">The time stamp to use for this edit</param>
  1571. /// <param name="content">The initial content of this edit</param>
  1572. /// <returns></returns>
  1573. internal static XElement CreateEdit(EditType t, DateTime edit_time, object content)
  1574. {
  1575. if (t == EditType.del)
  1576. {
  1577. foreach (object o in (IEnumerable<XElement>)content)
  1578. {
  1579. if (o is XElement)
  1580. {
  1581. XElement e = (o as XElement);
  1582. IEnumerable<XElement> ts = e.DescendantsAndSelf(XName.Get("t", DocX.w.NamespaceName));
  1583. for (int i = 0; i < ts.Count(); i++)
  1584. {
  1585. XElement text = ts.ElementAt(i);
  1586. text.ReplaceWith(new XElement(DocX.w + "delText", text.Attributes(), text.Value));
  1587. }
  1588. }
  1589. }
  1590. }
  1591. return
  1592. (
  1593. new XElement(DocX.w + t.ToString(),
  1594. new XAttribute(DocX.w + "id", 0),
  1595. new XAttribute(DocX.w + "author", WindowsIdentity.GetCurrent().Name),
  1596. new XAttribute(DocX.w + "date", edit_time),
  1597. content)
  1598. );
  1599. }
  1600. internal Run GetFirstRunEffectedByEdit(int index, EditType type = EditType.ins)
  1601. {
  1602. int len = HelperFunctions.GetText(Xml).Length;
  1603. // Make sure we are looking within an acceptable index range.
  1604. if (index < 0 || ((type == EditType.ins && index > len) || (type == EditType.del && index >= len)))
  1605. throw new ArgumentOutOfRangeException();
  1606. // Need some memory that can be updated by the recursive search for the XElement to Split.
  1607. int count = 0;
  1608. Run theOne = null;
  1609. GetFirstRunEffectedByEditRecursive(Xml, index, ref count, ref theOne, type);
  1610. return theOne;
  1611. }
  1612. internal void GetFirstRunEffectedByEditRecursive(XElement Xml, int index, ref int count, ref Run theOne, EditType type)
  1613. {
  1614. count += HelperFunctions.GetSize(Xml);
  1615. // If the EditType is deletion then we must return the next blah
  1616. if (count > 0 && ((type == EditType.del && count > index) || (type == EditType.ins && count >= index)))
  1617. {
  1618. // Correct the index
  1619. foreach (XElement e in Xml.ElementsBeforeSelf())
  1620. count -= HelperFunctions.GetSize(e);
  1621. count -= HelperFunctions.GetSize(Xml);
  1622. // We have found the element, now find the run it belongs to.
  1623. while ((Xml.Name.LocalName != "r") && (Xml.Name.LocalName != "pPr"))
  1624. Xml = Xml.Parent;
  1625. theOne = new Run(Document, Xml, count);
  1626. return;
  1627. }
  1628. if (Xml.HasElements)
  1629. foreach (XElement e in Xml.Elements())
  1630. if (theOne == null)
  1631. GetFirstRunEffectedByEditRecursive(e, index, ref count, ref theOne, type);
  1632. }
  1633. /// <!--
  1634. /// Bug found and fixed by krugs525 on August 12 2009.
  1635. /// Use TFS compare to see exact code change.
  1636. /// -->
  1637. static internal int GetElementTextLength(XElement run)
  1638. {
  1639. int count = 0;
  1640. if (run == null)
  1641. return count;
  1642. foreach (var d in run.Descendants())
  1643. {
  1644. switch (d.Name.LocalName)
  1645. {
  1646. case "tab":
  1647. if (d.Parent.Name.LocalName != "tabs")
  1648. goto case "br"; break;
  1649. case "br": count++; break;
  1650. case "t": goto case "delText";
  1651. case "delText": count += d.Value.Length; break;
  1652. default: break;
  1653. }
  1654. }
  1655. return count;
  1656. }
  1657. internal XElement[] SplitEdit(XElement edit, int index, EditType type)
  1658. {
  1659. Run run = GetFirstRunEffectedByEdit(index, type);
  1660. XElement[] splitRun = Run.SplitRun(run, index, type);
  1661. XElement splitLeft = new XElement(edit.Name, edit.Attributes(), run.Xml.ElementsBeforeSelf(), splitRun[0]);
  1662. if (GetElementTextLength(splitLeft) == 0)
  1663. splitLeft = null;
  1664. XElement splitRight = new XElement(edit.Name, edit.Attributes(), splitRun[1], run.Xml.ElementsAfterSelf());
  1665. if (GetElementTextLength(splitRight) == 0)
  1666. splitRight = null;
  1667. return
  1668. (
  1669. new XElement[]
  1670. {
  1671. splitLeft,
  1672. splitRight
  1673. }
  1674. );
  1675. }
  1676. /// <summary>
  1677. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1678. /// </summary>
  1679. /// <example>
  1680. /// <code>
  1681. /// // Create a document using a relative filename.
  1682. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1683. /// {
  1684. /// // Create a text formatting.
  1685. /// Formatting f = new Formatting();
  1686. /// f.FontColor = Color.Red;
  1687. /// f.Size = 30;
  1688. ///
  1689. /// // Iterate through the Paragraphs in this document.
  1690. /// foreach (Paragraph p in document.Paragraphs)
  1691. /// {
  1692. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1693. /// p.InsertText("Start: ", true, f);
  1694. /// }
  1695. ///
  1696. /// // Save all changes made to this document.
  1697. /// document.Save();
  1698. /// }// Release this document from memory.
  1699. /// </code>
  1700. /// </example>
  1701. /// <example>
  1702. /// Inserting tabs using the \t switch.
  1703. /// <code>
  1704. /// // Create a document using a relative filename.
  1705. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1706. /// {
  1707. /// // Create a text formatting.
  1708. /// Formatting f = new Formatting();
  1709. /// f.FontColor = Color.Red;
  1710. /// f.Size = 30;
  1711. ///
  1712. /// // Iterate through the paragraphs in this document.
  1713. /// foreach (Paragraph p in document.Paragraphs)
  1714. /// {
  1715. /// // Insert the string "\tEnd" at the end of every paragraph and flag it as a change.
  1716. /// p.InsertText("\tEnd", true, f);
  1717. /// }
  1718. ///
  1719. /// // Save all changes made to this document.
  1720. /// document.Save();
  1721. /// }// Release this document from memory.
  1722. /// </code>
  1723. /// </example>
  1724. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1725. /// <seealso cref="Paragraph.RemoveText(int, int, bool, bool)"/>
  1726. /// <param name="value">The System.String to insert.</param>
  1727. /// <param name="trackChanges">Flag this insert as a change.</param>
  1728. /// <param name="formatting">The text formatting.</param>
  1729. public void InsertText(string value, bool trackChanges = false, Formatting formatting = null)
  1730. {
  1731. // Default values for optional parameters must be compile time constants.
  1732. // Would have like to write 'public void InsertText(string value, bool trackChanges = false, Formatting formatting = new Formatting())
  1733. if (formatting == null) formatting = new Formatting();
  1734. List<XElement> newRuns = HelperFunctions.FormatInput(value, formatting.Xml);
  1735. Xml.Add(newRuns);
  1736. HelperFunctions.RenumberIDs(Document);
  1737. }
  1738. /// <summary>
  1739. /// Inserts a specified instance of System.String into a Novacode.DocX.Paragraph at a specified index position.
  1740. /// </summary>
  1741. /// <example>
  1742. /// <code>
  1743. /// // Create a document using a relative filename.
  1744. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1745. /// {
  1746. /// // Create a text formatting.
  1747. /// Formatting f = new Formatting();
  1748. /// f.FontColor = Color.Red;
  1749. /// f.Size = 30;
  1750. ///
  1751. /// // Iterate through the Paragraphs in this document.
  1752. /// foreach (Paragraph p in document.Paragraphs)
  1753. /// {
  1754. /// // Insert the string "Start: " at the begining of every Paragraph and flag it as a change.
  1755. /// p.InsertText(0, "Start: ", true, f);
  1756. /// }
  1757. ///
  1758. /// // Save all changes made to this document.
  1759. /// document.Save();
  1760. /// }// Release this document from memory.
  1761. /// </code>
  1762. /// </example>
  1763. /// <example>
  1764. /// Inserting tabs using the \t switch.
  1765. /// <code>
  1766. /// // Create a document using a relative filename.
  1767. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  1768. /// {
  1769. /// // Create a text formatting.
  1770. /// Formatting f = new Formatting();
  1771. /// f.FontColor = Color.Red;
  1772. /// f.Size = 30;
  1773. ///
  1774. /// // Iterate through the paragraphs in this document.
  1775. /// foreach (Paragraph p in document.Paragraphs)
  1776. /// {
  1777. /// // Insert the string "\tStart:\t" at the begining of every paragraph and flag it as a change.
  1778. /// p.InsertText(0, "\tStart:\t", true, f);
  1779. /// }
  1780. ///
  1781. /// // Save all changes made to this document.
  1782. /// document.Save();
  1783. /// }// Release this document from memory.
  1784. /// </code>
  1785. /// </example>
  1786. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  1787. /// <seealso cref="Paragraph.RemoveText(int, int, bool, bool)"/>
  1788. /// <param name="index">The index position of the insertion.</param>
  1789. /// <param name="value">The System.String to insert.</param>
  1790. /// <param name="trackChanges">Flag this insert as a change.</param>
  1791. /// <param name="formatting">The text formatting.</param>
  1792. public void InsertText(int index, string value, bool trackChanges = false, Formatting formatting = null)
  1793. {
  1794. // Timestamp to mark the start of insert
  1795. DateTime now = DateTime.Now;
  1796. DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  1797. // Get the first run effected by this Insert
  1798. Run run = GetFirstRunEffectedByEdit(index);
  1799. if (run == null)
  1800. {
  1801. object insert;
  1802. if (formatting != null) //not sure how to get original formatting here when run == null
  1803. insert = HelperFunctions.FormatInput(value, formatting.Xml);
  1804. else
  1805. insert = HelperFunctions.FormatInput(value, null);
  1806. if (trackChanges)
  1807. insert = CreateEdit(EditType.ins, insert_datetime, insert);
  1808. Xml.Add(insert);
  1809. }
  1810. else
  1811. {
  1812. object newRuns;
  1813. var rprel = run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName));
  1814. if (formatting != null)
  1815. {
  1816. //merge 2 formattings properly
  1817. Formatting finfmt = null;
  1818. Formatting oldfmt = null;
  1819. if (rprel != null)
  1820. {
  1821. oldfmt = Formatting.Parse(rprel);
  1822. }
  1823. if (oldfmt != null)
  1824. {
  1825. finfmt = oldfmt.Clone();
  1826. if (formatting.Bold.HasValue) { finfmt.Bold = formatting.Bold; }
  1827. if (formatting.CapsStyle.HasValue) { finfmt.CapsStyle = formatting.CapsStyle; }
  1828. if (formatting.FontColor.HasValue) { finfmt.FontColor = formatting.FontColor; }
  1829. finfmt.FontFamily = formatting.FontFamily;
  1830. if (formatting.Hidden.HasValue) { finfmt.Hidden = formatting.Hidden; }
  1831. if (formatting.Highlight.HasValue) { finfmt.Highlight = formatting.Highlight; }
  1832. if (formatting.Italic.HasValue) { finfmt.Italic = formatting.Italic; }
  1833. if (formatting.Kerning.HasValue) { finfmt.Kerning = formatting.Kerning; }
  1834. finfmt.Language = formatting.Language;
  1835. if (formatting.Misc.HasValue) { finfmt.Misc = formatting.Misc; }
  1836. if (formatting.PercentageScale.HasValue) { finfmt.PercentageScale = formatting.PercentageScale; }
  1837. if (formatting.Position.HasValue) { finfmt.Position = formatting.Position; }
  1838. if (formatting.Script.HasValue) { finfmt.Script = formatting.Script; }
  1839. if (formatting.Size.HasValue) { finfmt.Size = formatting.Size; }
  1840. if (formatting.Spacing.HasValue) { finfmt.Spacing = formatting.Spacing; }
  1841. if (formatting.StrikeThrough.HasValue) { finfmt.StrikeThrough = formatting.StrikeThrough; }
  1842. if (formatting.UnderlineColor.HasValue) { finfmt.UnderlineColor = formatting.UnderlineColor; }
  1843. if (formatting.UnderlineStyle.HasValue) { finfmt.UnderlineStyle = formatting.UnderlineStyle; }
  1844. }
  1845. else
  1846. {
  1847. finfmt = formatting;
  1848. }
  1849. newRuns = HelperFunctions.FormatInput(value, finfmt.Xml);
  1850. }
  1851. else
  1852. {
  1853. newRuns = HelperFunctions.FormatInput(value, rprel);
  1854. }
  1855. // The parent of this Run
  1856. XElement parentElement = run.Xml.Parent;
  1857. switch (parentElement.Name.LocalName)
  1858. {
  1859. case "ins":
  1860. {
  1861. // The datetime that this ins was created
  1862. DateTime parent_ins_date = DateTime.Parse(parentElement.Attribute(XName.Get("date", DocX.w.NamespaceName)).Value);
  1863. /*
  1864. * Special case: You want to track changes,
  1865. * and the first Run effected by this insert
  1866. * has a datetime stamp equal to now.
  1867. */
  1868. if (trackChanges && parent_ins_date.CompareTo(insert_datetime) == 0)
  1869. {
  1870. /*
  1871. * Inserting into a non edit and this special case, is the same procedure.
  1872. */
  1873. goto default;
  1874. }
  1875. /*
  1876. * If not the special case above,
  1877. * then inserting into an ins or a del, is the same procedure.
  1878. */
  1879. goto case "del";
  1880. }
  1881. case "del":
  1882. {
  1883. object insert = newRuns;
  1884. if (trackChanges)
  1885. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1886. // Split this Edit at the point you want to insert
  1887. XElement[] splitEdit = SplitEdit(parentElement, index, EditType.ins);
  1888. // Replace the origional run
  1889. parentElement.ReplaceWith
  1890. (
  1891. splitEdit[0],
  1892. insert,
  1893. splitEdit[1]
  1894. );
  1895. break;
  1896. }
  1897. default:
  1898. {
  1899. object insert = newRuns;
  1900. if (trackChanges && !parentElement.Name.LocalName.Equals("ins"))
  1901. insert = CreateEdit(EditType.ins, insert_datetime, newRuns);
  1902. // Special case to deal with Page Number elements.
  1903. //if (parentElement.Name.LocalName.Equals("fldSimple"))
  1904. // parentElement.AddBeforeSelf(insert);
  1905. else
  1906. {
  1907. // Split this run at the point you want to insert
  1908. XElement[] splitRun = Run.SplitRun(run, index);
  1909. // Replace the origional run
  1910. run.Xml.ReplaceWith
  1911. (
  1912. splitRun[0],
  1913. insert,
  1914. splitRun[1]
  1915. );
  1916. }
  1917. break;
  1918. }
  1919. }
  1920. }
  1921. HelperFunctions.RenumberIDs(Document);
  1922. }
  1923. /// <summary>
  1924. /// For use with Append() and AppendLine()
  1925. /// </summary>
  1926. /// <returns>This Paragraph in curent culture</returns>
  1927. /// <example>
  1928. /// Add a new Paragraph with russian text to this document and then set language of text to local culture.
  1929. /// <code>
  1930. /// // Load a document.
  1931. /// using (DocX document = DocX.Create(@"Test.docx"))
  1932. /// {
  1933. /// // Insert a new Paragraph with russian text and set curent local culture to it.
  1934. /// Paragraph p = document.InsertParagraph("Привет мир!").CurentCulture();
  1935. ///
  1936. /// // Save this document.
  1937. /// document.Save();
  1938. /// }
  1939. /// </code>
  1940. /// </example>
  1941. public Paragraph CurentCulture()
  1942. {
  1943. ApplyTextFormattingProperty(XName.Get("lang", DocX.w.NamespaceName),
  1944. string.Empty,
  1945. new XAttribute(XName.Get("val", DocX.w.NamespaceName), CultureInfo.CurrentCulture.Name));
  1946. return this;
  1947. }
  1948. /// <summary>
  1949. /// For use with Append() and AppendLine()
  1950. /// </summary>
  1951. /// <param name="culture">The CultureInfo for text</param>
  1952. /// <returns>This Paragraph in curent culture</returns>
  1953. /// <example>
  1954. /// Add a new Paragraph with russian text to this document and then set language of text to local culture.
  1955. /// <code>
  1956. /// // Load a document.
  1957. /// using (DocX document = DocX.Create(@"Test.docx"))
  1958. /// {
  1959. /// // Insert a new Paragraph with russian text and set specific culture to it.
  1960. /// Paragraph p = document.InsertParagraph("Привет мир").Culture(CultureInfo.CreateSpecificCulture("ru-RU"));
  1961. ///
  1962. /// // Save this document.
  1963. /// document.Save();
  1964. /// }
  1965. /// </code>
  1966. /// </example>
  1967. public Paragraph Culture(CultureInfo culture)
  1968. {
  1969. ApplyTextFormattingProperty(XName.Get("lang", DocX.w.NamespaceName), string.Empty,
  1970. new XAttribute(XName.Get("val", DocX.w.NamespaceName), culture.Name));
  1971. return this;
  1972. }
  1973. /// <summary>
  1974. /// Append text to this Paragraph.
  1975. /// </summary>
  1976. /// <param name="text">The text to append.</param>
  1977. /// <returns>This Paragraph with the new text appened.</returns>
  1978. /// <example>
  1979. /// Add a new Paragraph to this document and then append some text to it.
  1980. /// <code>
  1981. /// // Load a document.
  1982. /// using (DocX document = DocX.Create(@"Test.docx"))
  1983. /// {
  1984. /// // Insert a new Paragraph and Append some text to it.
  1985. /// Paragraph p = document.InsertParagraph().Append("Hello World!!!");
  1986. ///
  1987. /// // Save this document.
  1988. /// document.Save();
  1989. /// }
  1990. /// </code>
  1991. /// </example>
  1992. public Paragraph Append(string text)
  1993. {
  1994. List<XElement> newRuns = HelperFunctions.FormatInput(text, null);
  1995. Xml.Add(newRuns);
  1996. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Reverse().Take(newRuns.Count()).ToList();
  1997. return this;
  1998. }
  1999. public void InsertHorizontalLine(string lineType = "single", int size = 6, int space = 1, string color = "auto")
  2000. {
  2001. var pPr = this.GetOrCreate_pPr();
  2002. var border = pPr.Element(XName.Get("pBdr", DocX.w.NamespaceName));
  2003. if (border == null)
  2004. {
  2005. pPr.Add(new XElement(XName.Get("pBdr", DocX.w.NamespaceName)));
  2006. border = pPr.Element(XName.Get("pBdr", DocX.w.NamespaceName));
  2007. border.Add(new XElement(XName.Get("bottom", DocX.w.NamespaceName)));
  2008. var bottom = border.Element(XName.Get("bottom", DocX.w.NamespaceName));
  2009. bottom.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), lineType);
  2010. bottom.SetAttributeValue(XName.Get("sz", DocX.w.NamespaceName), size.ToString());
  2011. bottom.SetAttributeValue(XName.Get("space", DocX.w.NamespaceName), space.ToString());
  2012. bottom.SetAttributeValue(XName.Get("color", DocX.w.NamespaceName), color);
  2013. }
  2014. }
  2015. /// <summary>
  2016. /// Append a hyperlink to a Paragraph.
  2017. /// </summary>
  2018. /// <param name="h">The hyperlink to append.</param>
  2019. /// <returns>The Paragraph with the hyperlink appended.</returns>
  2020. /// <example>
  2021. /// Creates a Paragraph with some text and a hyperlink.
  2022. /// <code>
  2023. /// // Create a document.
  2024. /// using (DocX document = DocX.Create(@"Test.docx"))
  2025. /// {
  2026. /// // Add a hyperlink to this document.
  2027. /// Hyperlink h = document.AddHyperlink("Google", new Uri("http://www.google.com"));
  2028. ///
  2029. /// // Add a new Paragraph to this document.
  2030. /// Paragraph p = document.InsertParagraph();
  2031. /// p.Append("My favourite search engine is ");
  2032. /// p.AppendHyperlink(h);
  2033. /// p.Append(", I think it's great.");
  2034. ///
  2035. /// // Save all changes made to this document.
  2036. /// document.Save();
  2037. /// }
  2038. /// </code>
  2039. /// </example>
  2040. public Paragraph AppendHyperlink(Hyperlink h)
  2041. {
  2042. // Convert the path of this mainPart to its equilivant rels file path.
  2043. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  2044. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  2045. // Check to see if the rels file exists and create it if not.
  2046. if (!Document.package.PartExists(rels_path))
  2047. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  2048. // Check to see if a rel for this Hyperlink exists, create it if not.
  2049. var Id = GetOrGenerateRel(h);
  2050. Xml.Add(h.Xml);
  2051. Xml.Elements().Last().SetAttributeValue(DocX.r + "id", Id);
  2052. this.runs = Xml.Elements().Last().Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  2053. return this;
  2054. }
  2055. /// <summary>
  2056. /// Add an image to a document, create a custom view of that image (picture) and then insert it into a Paragraph using append.
  2057. /// </summary>
  2058. /// <param name="p">The Picture to append.</param>
  2059. /// <returns>The Paragraph with the Picture now appended.</returns>
  2060. /// <example>
  2061. /// Add an image to a document, create a custom view of that image (picture) and then insert it into a Paragraph using append.
  2062. /// <code>
  2063. /// using (DocX document = DocX.Create("Test.docx"))
  2064. /// {
  2065. /// // Add an image to the document.
  2066. /// Image i = document.AddImage(@"Image.jpg");
  2067. ///
  2068. /// // Create a picture i.e. (A custom view of an image)
  2069. /// Picture p = i.CreatePicture();
  2070. /// p.FlipHorizontal = true;
  2071. /// p.Rotation = 10;
  2072. ///
  2073. /// // Create a new Paragraph.
  2074. /// Paragraph par = document.InsertParagraph();
  2075. ///
  2076. /// // Append content to the Paragraph.
  2077. /// par.Append("Here is a cool picture")
  2078. /// .AppendPicture(p)
  2079. /// .Append(" don't you think so?");
  2080. ///
  2081. /// // Save all changes made to this document.
  2082. /// document.Save();
  2083. /// }
  2084. /// </code>
  2085. /// </example>
  2086. public Paragraph AppendPicture(Picture p)
  2087. {
  2088. // Convert the path of this mainPart to its equilivant rels file path.
  2089. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  2090. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  2091. // Check to see if the rels file exists and create it if not.
  2092. if (!Document.package.PartExists(rels_path))
  2093. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  2094. // Check to see if a rel for this Picture exists, create it if not.
  2095. var Id = GetOrGenerateRel(p);
  2096. // Add the Picture Xml to the end of the Paragragraph Xml.
  2097. Xml.Add(p.Xml);
  2098. // Extract the attribute id from the Pictures Xml.
  2099. XAttribute a_id =
  2100. (
  2101. from e in Xml.Elements().Last().Descendants()
  2102. where e.Name.LocalName.Equals("blip")
  2103. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"))
  2104. ).Single();
  2105. // Set its value to the Pictures relationships id.
  2106. a_id.SetValue(Id);
  2107. // For formatting such as .Bold()
  2108. this.runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Reverse().Take(p.Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).Count()).ToList();
  2109. return this;
  2110. }
  2111. /// <summary>
  2112. /// Add an equation to a document.
  2113. /// </summary>
  2114. /// <param name="equation">The Equation to append.</param>
  2115. /// <returns>The Paragraph with the Equation now appended.</returns>
  2116. /// <example>
  2117. /// Add an equation to a document.
  2118. /// <code>
  2119. /// using (DocX document = DocX.Create("Test.docx"))
  2120. /// {
  2121. /// // Add an equation to the document.
  2122. /// document.AddEquation("x=y+z");
  2123. ///
  2124. /// // Save all changes made to this document.
  2125. /// document.Save();
  2126. /// }
  2127. /// </code>
  2128. /// </example>
  2129. public Paragraph AppendEquation(String equation)
  2130. {
  2131. // Create equation element
  2132. XElement oMathPara =
  2133. new XElement
  2134. (
  2135. XName.Get("oMathPara", DocX.m.NamespaceName),
  2136. new XElement
  2137. (
  2138. XName.Get("oMath", DocX.m.NamespaceName),
  2139. new XElement
  2140. (
  2141. XName.Get("r", DocX.w.NamespaceName),
  2142. new Formatting() { FontFamily = new Font("Cambria Math") }.Xml, // create formatting
  2143. new XElement(XName.Get("t", DocX.m.NamespaceName), equation) // create equation string
  2144. )
  2145. )
  2146. );
  2147. // Add equation element into paragraph xml and update runs collection
  2148. Xml.Add(oMathPara);
  2149. runs = Xml.Elements(XName.Get("oMathPara", DocX.m.NamespaceName)).ToList();
  2150. // Return paragraph with equation
  2151. return this;
  2152. }
  2153. public bool ValidateBookmark(string bookmarkName)
  2154. {
  2155. return GetBookmarks().Any(b => b.Name.Equals(bookmarkName));
  2156. }
  2157. public Paragraph AppendBookmark(String bookmarkName)
  2158. {
  2159. XElement wBookmarkStart = new XElement(
  2160. XName.Get("bookmarkStart", DocX.w.NamespaceName),
  2161. new XAttribute(XName.Get("id", DocX.w.NamespaceName), 0),
  2162. new XAttribute(XName.Get("name", DocX.w.NamespaceName), bookmarkName));
  2163. Xml.Add(wBookmarkStart);
  2164. XElement wBookmarkEnd = new XElement(
  2165. XName.Get("bookmarkEnd", DocX.w.NamespaceName),
  2166. new XAttribute(XName.Get("id", DocX.w.NamespaceName), 0),
  2167. new XAttribute(XName.Get("name", DocX.w.NamespaceName), bookmarkName));
  2168. Xml.Add(wBookmarkEnd);
  2169. return this;
  2170. }
  2171. public IEnumerable<Bookmark> GetBookmarks()
  2172. {
  2173. return Xml.Descendants(XName.Get("bookmarkStart", DocX.w.NamespaceName))
  2174. .Select(x => x.Attribute(XName.Get("name", DocX.w.NamespaceName)))
  2175. .Select(x => new Bookmark
  2176. {
  2177. Name = x.Value,
  2178. Paragraph = this
  2179. });
  2180. }
  2181. public void InsertAtBookmark(string toInsert, string bookmarkName)
  2182. {
  2183. var bookmark = Xml.Descendants(XName.Get("bookmarkStart", DocX.w.NamespaceName))
  2184. .Where(x => x.Attribute(XName.Get("name", DocX.w.NamespaceName)).Value == bookmarkName).SingleOrDefault();
  2185. if (bookmark != null)
  2186. {
  2187. var run = HelperFunctions.FormatInput(toInsert, null);
  2188. bookmark.AddBeforeSelf(run);
  2189. runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  2190. HelperFunctions.RenumberIDs(Document);
  2191. }
  2192. }
  2193. public void ReplaceAtBookmark(string toInsert, string bookmarkName)
  2194. {
  2195. XElement bookmark = Xml.Descendants(XName.Get("bookmarkStart", DocX.w.NamespaceName))
  2196. .Where(x => x.Attribute(XName.Get("name", DocX.w.NamespaceName)).Value == bookmarkName)
  2197. .SingleOrDefault();
  2198. if (bookmark == null)
  2199. return;
  2200. XNode nextNode = bookmark.NextNode;
  2201. XElement nextElement = nextNode as XElement;
  2202. while (nextElement == null || nextElement.Name.NamespaceName != DocX.w.NamespaceName || (nextElement.Name.LocalName != "r" && nextElement.Name.LocalName != "bookmarkEnd"))
  2203. {
  2204. nextNode = nextNode.NextNode;
  2205. nextElement = nextNode as XElement;
  2206. }
  2207. // Check if next element is a bookmarkEnd
  2208. if (nextElement.Name.LocalName == "bookmarkEnd")
  2209. {
  2210. ReplaceAtBookmark_Add(toInsert, bookmark);
  2211. return;
  2212. }
  2213. XElement contentElement = nextElement.Elements(XName.Get("t", DocX.w.NamespaceName)).FirstOrDefault();
  2214. if (contentElement == null)
  2215. {
  2216. ReplaceAtBookmark_Add(toInsert, bookmark);
  2217. return;
  2218. }
  2219. contentElement.Value = toInsert;
  2220. }
  2221. private void ReplaceAtBookmark_Add(string toInsert, XElement bookmark)
  2222. {
  2223. var run = HelperFunctions.FormatInput(toInsert, null);
  2224. bookmark.AddAfterSelf(run);
  2225. runs = Xml.Elements(XName.Get("r", DocX.w.NamespaceName)).ToList();
  2226. HelperFunctions.RenumberIDs(Document);
  2227. }
  2228. internal string GetOrGenerateRel(Picture p)
  2229. {
  2230. string image_uri_string = p.img.pr.TargetUri.OriginalString;
  2231. // Search for a relationship with a TargetUri that points at this Image.
  2232. string id = null;
  2233. foreach (var r in mainPart.GetRelationshipsByType(DocX.relationshipImage))
  2234. {
  2235. if (string.Equals(r.TargetUri.OriginalString, image_uri_string, StringComparison.Ordinal))
  2236. {
  2237. id = r.Id;
  2238. break;
  2239. }
  2240. }
  2241. // If such a relation doesn't exist, create one.
  2242. if (id == null)
  2243. {
  2244. // Check to see if a relationship for this Picture exists and create it if not.
  2245. PackageRelationship pr = mainPart.CreateRelationship(p.img.pr.TargetUri, TargetMode.Internal, DocX.relationshipImage);
  2246. id = pr.Id;
  2247. }
  2248. return id;
  2249. }
  2250. internal string GetOrGenerateRel(Hyperlink h)
  2251. {
  2252. string image_uri_string = h.Uri.OriginalString;
  2253. // Search for a relationship with a TargetUri that points at this Image.
  2254. var Id =
  2255. (
  2256. from r in mainPart.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
  2257. where r.TargetUri.OriginalString == image_uri_string
  2258. select r.Id
  2259. ).SingleOrDefault();
  2260. // If such a relation dosen't exist, create one.
  2261. if (Id == null)
  2262. {
  2263. // Check to see if a relationship for this Picture exists and create it if not.
  2264. PackageRelationship pr = mainPart.CreateRelationship(h.Uri, TargetMode.External, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
  2265. Id = pr.Id;
  2266. }
  2267. return Id;
  2268. }
  2269. /// <summary>
  2270. /// Insert a Picture into a Paragraph at the given text index.
  2271. /// If not index is provided defaults to 0.
  2272. /// </summary>
  2273. /// <param name="p">The Picture to insert.</param>
  2274. /// <param name="index">The text index to insert at.</param>
  2275. /// <returns>The modified Paragraph.</returns>
  2276. /// <example>
  2277. /// <code>
  2278. ///Load test document.
  2279. ///using (DocX document = DocX.Create("Test.docx"))
  2280. ///{
  2281. /// // Add Headers and Footers into this document.
  2282. /// document.AddHeaders();
  2283. /// document.AddFooters();
  2284. /// document.DifferentFirstPage = true;
  2285. /// document.DifferentOddAndEvenPages = true;
  2286. ///
  2287. /// // Add an Image to this document.
  2288. /// Novacode.Image img = document.AddImage(directory_documents + "purple.png");
  2289. ///
  2290. /// // Create a Picture from this Image.
  2291. /// Picture pic = img.CreatePicture();
  2292. ///
  2293. /// // Main document.
  2294. /// Paragraph p0 = document.InsertParagraph("Hello");
  2295. /// p0.InsertPicture(pic, 3);
  2296. ///
  2297. /// // Header first.
  2298. /// Paragraph p1 = document.Headers.first.InsertParagraph("----");
  2299. /// p1.InsertPicture(pic, 2);
  2300. ///
  2301. /// // Header odd.
  2302. /// Paragraph p2 = document.Headers.odd.InsertParagraph("----");
  2303. /// p2.InsertPicture(pic, 2);
  2304. ///
  2305. /// // Header even.
  2306. /// Paragraph p3 = document.Headers.even.InsertParagraph("----");
  2307. /// p3.InsertPicture(pic, 2);
  2308. ///
  2309. /// // Footer first.
  2310. /// Paragraph p4 = document.Footers.first.InsertParagraph("----");
  2311. /// p4.InsertPicture(pic, 2);
  2312. ///
  2313. /// // Footer odd.
  2314. /// Paragraph p5 = document.Footers.odd.InsertParagraph("----");
  2315. /// p5.InsertPicture(pic, 2);
  2316. ///
  2317. /// // Footer even.
  2318. /// Paragraph p6 = document.Footers.even.InsertParagraph("----");
  2319. /// p6.InsertPicture(pic, 2);
  2320. ///
  2321. /// // Save this document.
  2322. /// document.Save();
  2323. ///}
  2324. /// </code>
  2325. /// </example>
  2326. public Paragraph InsertPicture(Picture p, int index = 0)
  2327. {
  2328. // Convert the path of this mainPart to its equilivant rels file path.
  2329. string path = mainPart.Uri.OriginalString.Replace("/word/", "");
  2330. Uri rels_path = new Uri("/word/_rels/" + path + ".rels", UriKind.Relative);
  2331. // Check to see if the rels file exists and create it if not.
  2332. if (!Document.package.PartExists(rels_path))
  2333. HelperFunctions.CreateRelsPackagePart(Document, rels_path);
  2334. // Check to see if a rel for this Picture exists, create it if not.
  2335. var Id = GetOrGenerateRel(p);
  2336. XElement p_xml;
  2337. if (index == 0)
  2338. {
  2339. // Add this hyperlink as the last element.
  2340. Xml.AddFirst(p.Xml);
  2341. // Extract the picture back out of the DOM.
  2342. p_xml = (XElement)Xml.FirstNode;
  2343. }
  2344. else
  2345. {
  2346. // Get the first run effected by this Insert
  2347. Run run = GetFirstRunEffectedByEdit(index);
  2348. if (run == null)
  2349. {
  2350. // Add this picture as the last element.
  2351. Xml.Add(p.Xml);
  2352. // Extract the picture back out of the DOM.
  2353. p_xml = (XElement)Xml.LastNode;
  2354. }
  2355. else
  2356. {
  2357. // Split this run at the point you want to insert
  2358. XElement[] splitRun = Run.SplitRun(run, index);
  2359. // Replace the origional run.
  2360. run.Xml.ReplaceWith
  2361. (
  2362. splitRun[0],
  2363. p.Xml,
  2364. splitRun[1]
  2365. );
  2366. // Get the first run effected by this Insert
  2367. run = GetFirstRunEffectedByEdit(index);
  2368. // The picture has to be the next element, extract it back out of the DOM.
  2369. p_xml = (XElement)run.Xml.NextNode;
  2370. }
  2371. }
  2372. // Extract the attribute id from the Pictures Xml.
  2373. XAttribute a_id =
  2374. (
  2375. from e in p_xml.Descendants()
  2376. where e.Name.LocalName.Equals("blip")
  2377. select e.Attribute(XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"))
  2378. ).Single();
  2379. // Set its value to the Pictures relationships id.
  2380. a_id.SetValue(Id);
  2381. return this;
  2382. }
  2383. /// <summary>
  2384. /// Append text on a new line to this Paragraph.
  2385. /// </summary>
  2386. /// <param name="text">The text to append.</param>
  2387. /// <returns>This Paragraph with the new text appened.</returns>
  2388. /// <example>
  2389. /// Add a new Paragraph to this document and then append a new line with some text to it.
  2390. /// <code>
  2391. /// // Load a document.
  2392. /// using (DocX document = DocX.Create(@"Test.docx"))
  2393. /// {
  2394. /// // Insert a new Paragraph and Append a new line with some text to it.
  2395. /// Paragraph p = document.InsertParagraph().AppendLine("Hello World!!!");
  2396. ///
  2397. /// // Save this document.
  2398. /// document.Save();
  2399. /// }
  2400. /// </code>
  2401. /// </example>
  2402. public Paragraph AppendLine(string text)
  2403. {
  2404. return Append("\n" + text);
  2405. }
  2406. /// <summary>
  2407. /// Append a new line to this Paragraph.
  2408. /// </summary>
  2409. /// <returns>This Paragraph with a new line appeneded.</returns>
  2410. /// <example>
  2411. /// Add a new Paragraph to this document and then append a new line to it.
  2412. /// <code>
  2413. /// // Load a document.
  2414. /// using (DocX document = DocX.Create(@"Test.docx"))
  2415. /// {
  2416. /// // Insert a new Paragraph and Append a new line with some text to it.
  2417. /// Paragraph p = document.InsertParagraph().AppendLine();
  2418. ///
  2419. /// // Save this document.
  2420. /// document.Save();
  2421. /// }
  2422. /// </code>
  2423. /// </example>
  2424. public Paragraph AppendLine()
  2425. {
  2426. return Append("\n");
  2427. }
  2428. internal void ApplyTextFormattingProperty(XName textFormatPropName, string value, object content)
  2429. {
  2430. XElement rPr = null;
  2431. if (runs.Count == 0)
  2432. {
  2433. XElement pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  2434. if (pPr == null)
  2435. {
  2436. Xml.AddFirst(new XElement(XName.Get("pPr", DocX.w.NamespaceName)));
  2437. pPr = Xml.Element(XName.Get("pPr", DocX.w.NamespaceName));
  2438. }
  2439. rPr = pPr.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2440. if (rPr == null)
  2441. {
  2442. pPr.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  2443. rPr = pPr.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2444. }
  2445. rPr.SetElementValue(textFormatPropName, value);
  2446. var last = rPr.Elements(textFormatPropName).Last();
  2447. if (content as XAttribute != null)//If content is an attribute
  2448. {
  2449. if (last.Attribute(((XAttribute)(content)).Name) == null)
  2450. {
  2451. last.Add(content); //Add this attribute if element doesn't have it
  2452. }
  2453. else
  2454. {
  2455. last.Attribute(((XAttribute)(content)).Name).Value = ((XAttribute)(content)).Value; //Apply value only if element already has it
  2456. }
  2457. }
  2458. return;
  2459. }
  2460. var contentIsListOfFontProperties = false;
  2461. var fontProps = content as IEnumerable;
  2462. if (fontProps != null)
  2463. {
  2464. foreach (object property in fontProps)
  2465. {
  2466. contentIsListOfFontProperties = (property as XAttribute != null);
  2467. }
  2468. }
  2469. foreach (XElement run in runs)
  2470. {
  2471. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2472. if (rPr == null)
  2473. {
  2474. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  2475. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2476. }
  2477. rPr.SetElementValue(textFormatPropName, value);
  2478. XElement last = rPr.Elements(textFormatPropName).Last();
  2479. if (contentIsListOfFontProperties) //if content is a list of attributes, as in the case when specifying a font family
  2480. {
  2481. foreach (object property in fontProps)
  2482. {
  2483. if (last.Attribute(((XAttribute)(property)).Name) == null)
  2484. {
  2485. last.Add(property); //Add this attribute if element doesn't have it
  2486. }
  2487. else
  2488. {
  2489. last.Attribute(((XAttribute)(property)).Name).Value =
  2490. ((XAttribute)(property)).Value; //Apply value only if element already has it
  2491. }
  2492. }
  2493. }
  2494. if (content as XAttribute != null)//If content is an attribute
  2495. {
  2496. if (last.Attribute(((XAttribute)(content)).Name) == null)
  2497. {
  2498. last.Add(content); //Add this attribute if element doesn't have it
  2499. }
  2500. else
  2501. {
  2502. last.Attribute(((XAttribute)(content)).Name).Value = ((XAttribute)(content)).Value; //Apply value only if element already has it
  2503. }
  2504. }
  2505. else
  2506. {
  2507. //IMPORTANT
  2508. //But what to do if it is not?
  2509. }
  2510. }
  2511. }
  2512. /// <summary>
  2513. /// For use with Append() and AppendLine()
  2514. /// </summary>
  2515. /// <returns>This Paragraph with the last appended text bold.</returns>
  2516. /// <example>
  2517. /// Append text to this Paragraph and then make it bold.
  2518. /// <code>
  2519. /// // Create a document.
  2520. /// using (DocX document = DocX.Create(@"Test.docx"))
  2521. /// {
  2522. /// // Insert a new Paragraph.
  2523. /// Paragraph p = document.InsertParagraph();
  2524. ///
  2525. /// p.Append("I am ")
  2526. /// .Append("Bold").Bold()
  2527. /// .Append(" I am not");
  2528. ///
  2529. /// // Save this document.
  2530. /// document.Save();
  2531. /// }// Release this document from memory.
  2532. /// </code>
  2533. /// </example>
  2534. public Paragraph Bold()
  2535. {
  2536. ApplyTextFormattingProperty(XName.Get("b", DocX.w.NamespaceName), string.Empty, null);
  2537. return this;
  2538. }
  2539. /// <summary>
  2540. /// For use with Append() and AppendLine()
  2541. /// </summary>
  2542. /// <returns>This Paragraph with the last appended text italic.</returns>
  2543. /// <example>
  2544. /// Append text to this Paragraph and then make it italic.
  2545. /// <code>
  2546. /// // Create a document.
  2547. /// using (DocX document = DocX.Create(@"Test.docx"))
  2548. /// {
  2549. /// // Insert a new Paragraph.
  2550. /// Paragraph p = document.InsertParagraph();
  2551. ///
  2552. /// p.Append("I am ")
  2553. /// .Append("Italic").Italic()
  2554. /// .Append(" I am not");
  2555. ///
  2556. /// // Save this document.
  2557. /// document.Save();
  2558. /// }// Release this document from memory.
  2559. /// </code>
  2560. /// </example>
  2561. public Paragraph Italic()
  2562. {
  2563. ApplyTextFormattingProperty(XName.Get("i", DocX.w.NamespaceName), string.Empty, null);
  2564. return this;
  2565. }
  2566. /// <summary>
  2567. /// For use with Append() and AppendLine()
  2568. /// </summary>
  2569. /// <param name="c">A color to use on the appended text.</param>
  2570. /// <returns>This Paragraph with the last appended text colored.</returns>
  2571. /// <example>
  2572. /// Append text to this Paragraph and then color it.
  2573. /// <code>
  2574. /// // Create a document.
  2575. /// using (DocX document = DocX.Create(@"Test.docx"))
  2576. /// {
  2577. /// // Insert a new Paragraph.
  2578. /// Paragraph p = document.InsertParagraph();
  2579. ///
  2580. /// p.Append("I am ")
  2581. /// .Append("Blue").Color(Color.Blue)
  2582. /// .Append(" I am not");
  2583. ///
  2584. /// // Save this document.
  2585. /// document.Save();
  2586. /// }// Release this document from memory.
  2587. /// </code>
  2588. /// </example>
  2589. public Paragraph Color(Color c)
  2590. {
  2591. ApplyTextFormattingProperty(XName.Get("color", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), c.ToHex()));
  2592. return this;
  2593. }
  2594. /// <summary>
  2595. /// For use with Append() and AppendLine()
  2596. /// </summary>
  2597. /// <param name="underlineStyle">The underline style to use for the appended text.</param>
  2598. /// <returns>This Paragraph with the last appended text underlined.</returns>
  2599. /// <example>
  2600. /// Append text to this Paragraph and then underline it.
  2601. /// <code>
  2602. /// // Create a document.
  2603. /// using (DocX document = DocX.Create(@"Test.docx"))
  2604. /// {
  2605. /// // Insert a new Paragraph.
  2606. /// Paragraph p = document.InsertParagraph();
  2607. ///
  2608. /// p.Append("I am ")
  2609. /// .Append("Underlined").UnderlineStyle(UnderlineStyle.doubleLine)
  2610. /// .Append(" I am not");
  2611. ///
  2612. /// // Save this document.
  2613. /// document.Save();
  2614. /// }// Release this document from memory.
  2615. /// </code>
  2616. /// </example>
  2617. public Paragraph UnderlineStyle(UnderlineStyle underlineStyle)
  2618. {
  2619. string value;
  2620. switch (underlineStyle)
  2621. {
  2622. case Novacode.UnderlineStyle.none: value = string.Empty; break;
  2623. case Novacode.UnderlineStyle.singleLine: value = "single"; break;
  2624. case Novacode.UnderlineStyle.doubleLine: value = "double"; break;
  2625. default: value = underlineStyle.ToString(); break;
  2626. }
  2627. ApplyTextFormattingProperty(XName.Get("u", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), value));
  2628. return this;
  2629. }
  2630. private Table followingTable;
  2631. ///<summary>
  2632. /// Returns table following the paragraph. Null if the following element isn't table.
  2633. ///</summary>
  2634. public Table FollowingTable
  2635. {
  2636. get
  2637. {
  2638. return followingTable;
  2639. }
  2640. internal set
  2641. {
  2642. followingTable = value;
  2643. }
  2644. }
  2645. /// <summary>
  2646. /// For use with Append() and AppendLine()
  2647. /// </summary>
  2648. /// <param name="fontSize">The font size to use for the appended text.</param>
  2649. /// <returns>This Paragraph with the last appended text resized.</returns>
  2650. /// <example>
  2651. /// Append text to this Paragraph and then resize it.
  2652. /// <code>
  2653. /// // Create a document.
  2654. /// using (DocX document = DocX.Create(@"Test.docx"))
  2655. /// {
  2656. /// // Insert a new Paragraph.
  2657. /// Paragraph p = document.InsertParagraph();
  2658. ///
  2659. /// p.Append("I am ")
  2660. /// .Append("Big").FontSize(20)
  2661. /// .Append(" I am not");
  2662. ///
  2663. /// // Save this document.
  2664. /// document.Save();
  2665. /// }// Release this document from memory.
  2666. /// </code>
  2667. /// </example>
  2668. public Paragraph FontSize(double fontSize)
  2669. {
  2670. double temp = fontSize * 2;
  2671. if (temp - (int)temp == 0)
  2672. {
  2673. if (!(fontSize > 0 && fontSize < 1639))
  2674. throw new ArgumentException("Size", "Value must be in the range 0 - 1638");
  2675. }
  2676. else
  2677. throw new ArgumentException("Size", "Value must be either a whole or half number, examples: 32, 32.5");
  2678. ApplyTextFormattingProperty(XName.Get("sz", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  2679. ApplyTextFormattingProperty(XName.Get("szCs", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), fontSize * 2));
  2680. return this;
  2681. }
  2682. /// <summary>
  2683. /// For use with Append() and AppendLine()
  2684. /// </summary>
  2685. /// <param name="fontName">The font to use for the appended text.</param>
  2686. /// <returns>This Paragraph with the last appended text's font changed.</returns>
  2687. public Paragraph Font(string fontName)
  2688. {
  2689. return Font(new Font(fontName));
  2690. }
  2691. /// <summary>
  2692. /// For use with Append() and AppendLine()
  2693. /// </summary>
  2694. /// <param name="fontFamily">The font to use for the appended text.</param>
  2695. /// <returns>This Paragraph with the last appended text's font changed.</returns>
  2696. /// <example>
  2697. /// Append text to this Paragraph and then change its font.
  2698. /// <code>
  2699. /// // Create a document.
  2700. /// using (DocX document = DocX.Create(@"Test.docx"))
  2701. /// {
  2702. /// // Insert a new Paragraph.
  2703. /// Paragraph p = document.InsertParagraph();
  2704. ///
  2705. /// p.Append("I am ")
  2706. /// .Append("Times new roman").Font(new FontFamily("Times new roman"))
  2707. /// .Append(" I am not");
  2708. ///
  2709. /// // Save this document.
  2710. /// document.Save();
  2711. /// }// Release this document from memory.
  2712. /// </code>
  2713. /// </example>
  2714. public Paragraph Font(Font fontFamily)
  2715. {
  2716. ApplyTextFormattingProperty
  2717. (
  2718. XName.Get("rFonts", DocX.w.NamespaceName),
  2719. string.Empty,
  2720. new[]
  2721. {
  2722. new XAttribute(XName.Get("ascii", DocX.w.NamespaceName), fontFamily.Name),
  2723. 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
  2724. 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
  2725. new XAttribute(XName.Get("eastAsia", DocX.w.NamespaceName), fontFamily.Name) // DOCX in china #57
  2726. }
  2727. );
  2728. return this;
  2729. }
  2730. /// <summary>
  2731. /// For use with Append() and AppendLine()
  2732. /// </summary>
  2733. /// <param name="capsStyle">The caps style to apply to the last appended text.</param>
  2734. /// <returns>This Paragraph with the last appended text's caps style changed.</returns>
  2735. /// <example>
  2736. /// Append text to this Paragraph and then set it to full caps.
  2737. /// <code>
  2738. /// // Create a document.
  2739. /// using (DocX document = DocX.Create(@"Test.docx"))
  2740. /// {
  2741. /// // Insert a new Paragraph.
  2742. /// Paragraph p = document.InsertParagraph();
  2743. ///
  2744. /// p.Append("I am ")
  2745. /// .Append("Capitalized").CapsStyle(CapsStyle.caps)
  2746. /// .Append(" I am not");
  2747. ///
  2748. /// // Save this document.
  2749. /// document.Save();
  2750. /// }// Release this document from memory.
  2751. /// </code>
  2752. /// </example>
  2753. public Paragraph CapsStyle(CapsStyle capsStyle)
  2754. {
  2755. switch (capsStyle)
  2756. {
  2757. case Novacode.CapsStyle.none:
  2758. break;
  2759. default:
  2760. {
  2761. ApplyTextFormattingProperty(XName.Get(capsStyle.ToString(), DocX.w.NamespaceName), string.Empty, null);
  2762. break;
  2763. }
  2764. }
  2765. return this;
  2766. }
  2767. /// <summary>
  2768. /// For use with Append() and AppendLine()
  2769. /// </summary>
  2770. /// <param name="script">The script style to apply to the last appended text.</param>
  2771. /// <returns>This Paragraph with the last appended text's script style changed.</returns>
  2772. /// <example>
  2773. /// Append text to this Paragraph and then set it to superscript.
  2774. /// <code>
  2775. /// // Create a document.
  2776. /// using (DocX document = DocX.Create(@"Test.docx"))
  2777. /// {
  2778. /// // Insert a new Paragraph.
  2779. /// Paragraph p = document.InsertParagraph();
  2780. ///
  2781. /// p.Append("I am ")
  2782. /// .Append("superscript").Script(Script.superscript)
  2783. /// .Append(" I am not");
  2784. ///
  2785. /// // Save this document.
  2786. /// document.Save();
  2787. /// }// Release this document from memory.
  2788. /// </code>
  2789. /// </example>
  2790. public Paragraph Script(Script script)
  2791. {
  2792. switch (script)
  2793. {
  2794. case Novacode.Script.none:
  2795. break;
  2796. default:
  2797. {
  2798. ApplyTextFormattingProperty(XName.Get("vertAlign", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), script.ToString()));
  2799. break;
  2800. }
  2801. }
  2802. return this;
  2803. }
  2804. /// <summary>
  2805. /// For use with Append() and AppendLine()
  2806. /// </summary>
  2807. ///<param name="highlight">The highlight to apply to the last appended text.</param>
  2808. /// <returns>This Paragraph with the last appended text highlighted.</returns>
  2809. /// <example>
  2810. /// Append text to this Paragraph and then highlight it.
  2811. /// <code>
  2812. /// // Create a document.
  2813. /// using (DocX document = DocX.Create(@"Test.docx"))
  2814. /// {
  2815. /// // Insert a new Paragraph.
  2816. /// Paragraph p = document.InsertParagraph();
  2817. ///
  2818. /// p.Append("I am ")
  2819. /// .Append("highlighted").Highlight(Highlight.green)
  2820. /// .Append(" I am not");
  2821. ///
  2822. /// // Save this document.
  2823. /// document.Save();
  2824. /// }// Release this document from memory.
  2825. /// </code>
  2826. /// </example>
  2827. public Paragraph Highlight(Highlight highlight)
  2828. {
  2829. switch (highlight)
  2830. {
  2831. case Novacode.Highlight.none:
  2832. break;
  2833. default:
  2834. {
  2835. ApplyTextFormattingProperty(XName.Get("highlight", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), highlight.ToString()));
  2836. break;
  2837. }
  2838. }
  2839. return this;
  2840. }
  2841. /// <summary>
  2842. /// For use with Append() and AppendLine()
  2843. /// </summary>
  2844. /// <param name="misc">The miscellaneous property to set.</param>
  2845. /// <returns>This Paragraph with the last appended text changed by a miscellaneous property.</returns>
  2846. /// <example>
  2847. /// Append text to this Paragraph and then apply a miscellaneous property.
  2848. /// <code>
  2849. /// // Create a document.
  2850. /// using (DocX document = DocX.Create(@"Test.docx"))
  2851. /// {
  2852. /// // Insert a new Paragraph.
  2853. /// Paragraph p = document.InsertParagraph();
  2854. ///
  2855. /// p.Append("I am ")
  2856. /// .Append("outlined").Misc(Misc.outline)
  2857. /// .Append(" I am not");
  2858. ///
  2859. /// // Save this document.
  2860. /// document.Save();
  2861. /// }// Release this document from memory.
  2862. /// </code>
  2863. /// </example>
  2864. public Paragraph Misc(Misc misc)
  2865. {
  2866. switch (misc)
  2867. {
  2868. case Novacode.Misc.none:
  2869. break;
  2870. case Novacode.Misc.outlineShadow:
  2871. {
  2872. ApplyTextFormattingProperty(XName.Get("outline", DocX.w.NamespaceName), string.Empty, null);
  2873. ApplyTextFormattingProperty(XName.Get("shadow", DocX.w.NamespaceName), string.Empty, null);
  2874. break;
  2875. }
  2876. case Novacode.Misc.engrave:
  2877. {
  2878. ApplyTextFormattingProperty(XName.Get("imprint", DocX.w.NamespaceName), string.Empty, null);
  2879. break;
  2880. }
  2881. default:
  2882. {
  2883. ApplyTextFormattingProperty(XName.Get(misc.ToString(), DocX.w.NamespaceName), string.Empty, null);
  2884. break;
  2885. }
  2886. }
  2887. return this;
  2888. }
  2889. /// <summary>
  2890. /// For use with Append() and AppendLine()
  2891. /// </summary>
  2892. /// <param name="strikeThrough">The strike through style to used on the last appended text.</param>
  2893. /// <returns>This Paragraph with the last appended text striked.</returns>
  2894. /// <example>
  2895. /// Append text to this Paragraph and then strike it.
  2896. /// <code>
  2897. /// // Create a document.
  2898. /// using (DocX document = DocX.Create(@"Test.docx"))
  2899. /// {
  2900. /// // Insert a new Paragraph.
  2901. /// Paragraph p = document.InsertParagraph();
  2902. ///
  2903. /// p.Append("I am ")
  2904. /// .Append("striked").StrikeThrough(StrikeThrough.doubleStrike)
  2905. /// .Append(" I am not");
  2906. ///
  2907. /// // Save this document.
  2908. /// document.Save();
  2909. /// }// Release this document from memory.
  2910. /// </code>
  2911. /// </example>
  2912. public Paragraph StrikeThrough(StrikeThrough strikeThrough)
  2913. {
  2914. string value;
  2915. switch (strikeThrough)
  2916. {
  2917. case Novacode.StrikeThrough.strike: value = "strike"; break;
  2918. case Novacode.StrikeThrough.doubleStrike: value = "dstrike"; break;
  2919. default: return this;
  2920. }
  2921. ApplyTextFormattingProperty(XName.Get(value, DocX.w.NamespaceName), string.Empty, null);
  2922. return this;
  2923. }
  2924. /// <summary>
  2925. /// For use with Append() and AppendLine()
  2926. /// </summary>
  2927. /// <param name="underlineColor">The underline color to use, if no underline is set, a single line will be used.</param>
  2928. /// <returns>This Paragraph with the last appended text underlined in a color.</returns>
  2929. /// <example>
  2930. /// Append text to this Paragraph and then underline it using a color.
  2931. /// <code>
  2932. /// // Create a document.
  2933. /// using (DocX document = DocX.Create(@"Test.docx"))
  2934. /// {
  2935. /// // Insert a new Paragraph.
  2936. /// Paragraph p = document.InsertParagraph();
  2937. ///
  2938. /// p.Append("I am ")
  2939. /// .Append("color underlined").UnderlineStyle(UnderlineStyle.dotted).UnderlineColor(Color.Orange)
  2940. /// .Append(" I am not");
  2941. ///
  2942. /// // Save this document.
  2943. /// document.Save();
  2944. /// }// Release this document from memory.
  2945. /// </code>
  2946. /// </example>
  2947. public Paragraph UnderlineColor(Color underlineColor)
  2948. {
  2949. foreach (XElement run in runs)
  2950. {
  2951. XElement rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2952. if (rPr == null)
  2953. {
  2954. run.AddFirst(new XElement(XName.Get("rPr", DocX.w.NamespaceName)));
  2955. rPr = run.Element(XName.Get("rPr", DocX.w.NamespaceName));
  2956. }
  2957. XElement u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  2958. if (u == null)
  2959. {
  2960. rPr.SetElementValue(XName.Get("u", DocX.w.NamespaceName), string.Empty);
  2961. u = rPr.Element(XName.Get("u", DocX.w.NamespaceName));
  2962. u.SetAttributeValue(XName.Get("val", DocX.w.NamespaceName), "single");
  2963. }
  2964. u.SetAttributeValue(XName.Get("color", DocX.w.NamespaceName), underlineColor.ToHex());
  2965. }
  2966. return this;
  2967. }
  2968. /// <summary>
  2969. /// For use with Append() and AppendLine()
  2970. /// </summary>
  2971. /// <returns>This Paragraph with the last appended text hidden.</returns>
  2972. /// <example>
  2973. /// Append text to this Paragraph and then hide it.
  2974. /// <code>
  2975. /// // Create a document.
  2976. /// using (DocX document = DocX.Create(@"Test.docx"))
  2977. /// {
  2978. /// // Insert a new Paragraph.
  2979. /// Paragraph p = document.InsertParagraph();
  2980. ///
  2981. /// p.Append("I am ")
  2982. /// .Append("hidden").Hide()
  2983. /// .Append(" I am not");
  2984. ///
  2985. /// // Save this document.
  2986. /// document.Save();
  2987. /// }// Release this document from memory.
  2988. /// </code>
  2989. /// </example>
  2990. public Paragraph Hide()
  2991. {
  2992. ApplyTextFormattingProperty(XName.Get("vanish", DocX.w.NamespaceName), string.Empty, null);
  2993. return this;
  2994. }
  2995. public float LineSpacing
  2996. {
  2997. get
  2998. {
  2999. XElement pPr = GetOrCreate_pPr();
  3000. XElement spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3001. if (spacing != null)
  3002. {
  3003. XAttribute line = spacing.Attribute(XName.Get("line", DocX.w.NamespaceName));
  3004. if (line != null)
  3005. {
  3006. float f;
  3007. if (float.TryParse(line.Value, out f))
  3008. return f / 20.0f;
  3009. }
  3010. }
  3011. return 1.1f * 20.0f;
  3012. }
  3013. set
  3014. {
  3015. Spacing(value);
  3016. }
  3017. }
  3018. /// <summary>
  3019. /// Set the linespacing for this paragraph manually.
  3020. /// </summary>
  3021. /// <param name="spacingType">The type of spacing to be set, can be either Before, After or Line (Standard line spacing).</param>
  3022. /// <param name="spacingFloat">A float value of the amount of spacing. Equals the value that van be set in Word using the "Line and Paragraph spacing" button.</param>
  3023. public void SetLineSpacing(LineSpacingType spacingType, float spacingFloat)
  3024. {
  3025. spacingFloat = spacingFloat * 240;
  3026. int spacingValue = (int)spacingFloat;
  3027. var pPr = this.GetOrCreate_pPr();
  3028. var spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3029. if (spacing == null)
  3030. {
  3031. pPr.Add(new XElement(XName.Get("spacing", DocX.w.NamespaceName)));
  3032. spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3033. }
  3034. string spacingTypeAttribute = "";
  3035. switch (spacingType)
  3036. {
  3037. case LineSpacingType.Line:
  3038. {
  3039. spacingTypeAttribute = "line";
  3040. break;
  3041. }
  3042. case LineSpacingType.Before:
  3043. {
  3044. spacingTypeAttribute = "before";
  3045. break;
  3046. }
  3047. case LineSpacingType.After:
  3048. {
  3049. spacingTypeAttribute = "after";
  3050. break;
  3051. }
  3052. }
  3053. spacing.SetAttributeValue(XName.Get(spacingTypeAttribute, DocX.w.NamespaceName), spacingValue);
  3054. }
  3055. /// <summary>
  3056. /// Set the linespacing for this paragraph using the Auto value.
  3057. /// </summary>
  3058. /// <param name="spacingType">The type of spacing to be set automatically. Using Auto will set both Before and After. None will remove any linespacing.</param>
  3059. public void SetLineSpacing(LineSpacingTypeAuto spacingType)
  3060. {
  3061. int spacingValue = 100;
  3062. var pPr = this.GetOrCreate_pPr();
  3063. var spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3064. if (spacingType.Equals(LineSpacingTypeAuto.None))
  3065. {
  3066. if (spacing != null)
  3067. {
  3068. spacing.Remove();
  3069. }
  3070. }
  3071. else
  3072. {
  3073. if (spacing == null)
  3074. {
  3075. pPr.Add(new XElement(XName.Get("spacing", DocX.w.NamespaceName)));
  3076. spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3077. }
  3078. string spacingTypeAttribute = "";
  3079. string autoSpacingTypeAttribute = "";
  3080. switch (spacingType)
  3081. {
  3082. case LineSpacingTypeAuto.AutoBefore:
  3083. {
  3084. spacingTypeAttribute = "before";
  3085. autoSpacingTypeAttribute = "beforeAutospacing";
  3086. break;
  3087. }
  3088. case LineSpacingTypeAuto.AutoAfter:
  3089. {
  3090. spacingTypeAttribute = "after";
  3091. autoSpacingTypeAttribute = "afterAutospacing";
  3092. break;
  3093. }
  3094. case LineSpacingTypeAuto.Auto:
  3095. {
  3096. spacingTypeAttribute = "before";
  3097. autoSpacingTypeAttribute = "beforeAutospacing";
  3098. spacing.SetAttributeValue(XName.Get("after", DocX.w.NamespaceName), spacingValue);
  3099. spacing.SetAttributeValue(XName.Get("afterAutospacing", DocX.w.NamespaceName), 1);
  3100. break;
  3101. }
  3102. }
  3103. spacing.SetAttributeValue(XName.Get(autoSpacingTypeAttribute, DocX.w.NamespaceName), 1);
  3104. spacing.SetAttributeValue(XName.Get(spacingTypeAttribute, DocX.w.NamespaceName), spacingValue);
  3105. }
  3106. }
  3107. public Paragraph Spacing(double spacing)
  3108. {
  3109. spacing *= 20;
  3110. if (spacing - (int)spacing == 0)
  3111. {
  3112. if (!(spacing > -1585 && spacing < 1585))
  3113. throw new ArgumentException("Spacing", "Value must be in the range: -1584 - 1584");
  3114. }
  3115. else
  3116. throw new ArgumentException("Spacing", "Value must be either a whole or acurate to one decimal, examples: 32, 32.1, 32.2, 32.9");
  3117. ApplyTextFormattingProperty(XName.Get("spacing", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), spacing));
  3118. return this;
  3119. }
  3120. public Paragraph SpacingBefore(double spacingBefore)
  3121. {
  3122. spacingBefore *= 20;
  3123. var pPr = GetOrCreate_pPr();
  3124. var spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3125. if (spacingBefore > 0)
  3126. {
  3127. if (spacing == null)
  3128. {
  3129. spacing = new XElement(XName.Get("spacing", DocX.w.NamespaceName));
  3130. pPr.Add(spacing);
  3131. }
  3132. var attr = spacing.Attribute(XName.Get("before", DocX.w.NamespaceName));
  3133. if (attr == null)
  3134. spacing.SetAttributeValue(XName.Get("before", DocX.w.NamespaceName), spacingBefore);
  3135. else
  3136. attr.SetValue(spacingBefore);
  3137. }
  3138. if (Math.Abs(spacingBefore) < 0.1f && spacing != null)
  3139. {
  3140. var attr = spacing.Attribute(XName.Get("before", DocX.w.NamespaceName));
  3141. attr.Remove();
  3142. if (!spacing.HasAttributes)
  3143. spacing.Remove();
  3144. }
  3145. return this;
  3146. }
  3147. public Paragraph SpacingAfter(double spacingAfter)
  3148. {
  3149. spacingAfter *= 20;
  3150. var pPr = GetOrCreate_pPr();
  3151. var spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3152. if (spacingAfter > 0)
  3153. {
  3154. if (spacing == null)
  3155. {
  3156. spacing = new XElement(XName.Get("spacing", DocX.w.NamespaceName));
  3157. pPr.Add(spacing);
  3158. }
  3159. var attr = spacing.Attribute(XName.Get("after", DocX.w.NamespaceName));
  3160. if (attr == null)
  3161. spacing.SetAttributeValue(XName.Get("after", DocX.w.NamespaceName), spacingAfter);
  3162. else
  3163. attr.SetValue(spacingAfter);
  3164. }
  3165. if (Math.Abs(spacingAfter) < 0.1f && spacing != null)
  3166. {
  3167. var attr = spacing.Attribute(XName.Get("after", DocX.w.NamespaceName));
  3168. attr.Remove();
  3169. if (!spacing.HasAttributes)
  3170. spacing.Remove();
  3171. }
  3172. //ApplyTextFormattingProperty(XName.Get("after", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), spacingAfter));
  3173. return this;
  3174. }
  3175. public Paragraph Kerning(int kerning)
  3176. {
  3177. if (!new int?[] { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 }.Contains(kerning))
  3178. 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");
  3179. ApplyTextFormattingProperty(XName.Get("kern", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), kerning * 2));
  3180. return this;
  3181. }
  3182. public Paragraph Position(double position)
  3183. {
  3184. if (!(position > -1585 && position < 1585))
  3185. throw new ArgumentOutOfRangeException("Position", "Value must be in the range -1585 - 1585");
  3186. ApplyTextFormattingProperty(XName.Get("position", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), position * 2));
  3187. return this;
  3188. }
  3189. public Paragraph PercentageScale(int percentageScale)
  3190. {
  3191. if (!(new int?[] { 200, 150, 100, 90, 80, 66, 50, 33 }).Contains(percentageScale))
  3192. throw new ArgumentOutOfRangeException("PercentageScale", "Value must be one of the following: 200, 150, 100, 90, 80, 66, 50 or 33");
  3193. ApplyTextFormattingProperty(XName.Get("w", DocX.w.NamespaceName), string.Empty, new XAttribute(XName.Get("val", DocX.w.NamespaceName), percentageScale));
  3194. return this;
  3195. }
  3196. /// <summary>
  3197. /// Append a field of type document property, this field will display the custom property cp, at the end of this paragraph.
  3198. /// </summary>
  3199. /// <param name="cp">The custom property to display.</param>
  3200. /// <param name="trackChanges"></param>
  3201. /// <param name="f">The formatting to use for this text.</param>
  3202. /// <example>
  3203. /// Create, add and display a custom property in a document.
  3204. /// <code>
  3205. /// // Load a document.
  3206. ///using (DocX document = DocX.Create("CustomProperty_Add.docx"))
  3207. ///{
  3208. /// // Add a few Custom Properties to this document.
  3209. /// document.AddCustomProperty(new CustomProperty("fname", "cathal"));
  3210. /// document.AddCustomProperty(new CustomProperty("age", 24));
  3211. /// document.AddCustomProperty(new CustomProperty("male", true));
  3212. /// document.AddCustomProperty(new CustomProperty("newyear2012", new DateTime(2012, 1, 1)));
  3213. /// document.AddCustomProperty(new CustomProperty("fav_num", 3.141592));
  3214. ///
  3215. /// // Insert a new Paragraph and append a load of DocProperties.
  3216. /// Paragraph p = document.InsertParagraph("fname: ")
  3217. /// .AppendDocProperty(document.CustomProperties["fname"])
  3218. /// .Append(", age: ")
  3219. /// .AppendDocProperty(document.CustomProperties["age"])
  3220. /// .Append(", male: ")
  3221. /// .AppendDocProperty(document.CustomProperties["male"])
  3222. /// .Append(", newyear2012: ")
  3223. /// .AppendDocProperty(document.CustomProperties["newyear2012"])
  3224. /// .Append(", fav_num: ")
  3225. /// .AppendDocProperty(document.CustomProperties["fav_num"]);
  3226. ///
  3227. /// // Save the changes to the document.
  3228. /// document.Save();
  3229. ///}
  3230. /// </code>
  3231. /// </example>
  3232. public Paragraph AppendDocProperty(CustomProperty cp, bool trackChanges = false, Formatting f = null)
  3233. {
  3234. this.InsertDocProperty(cp, trackChanges, f);
  3235. return this;
  3236. }
  3237. /// <summary>
  3238. /// Insert a field of type document property, this field will display the custom property cp, at the end of this paragraph.
  3239. /// </summary>
  3240. /// <param name="cp">The custom property to display.</param>
  3241. /// <param name="trackChanges"></param>
  3242. /// <param name="f">The formatting to use for this text.</param>
  3243. /// <example>
  3244. /// Create, add and display a custom property in a document.
  3245. /// <code>
  3246. /// // Load a document
  3247. /// using (DocX document = DocX.Create(@"Test.docx"))
  3248. /// {
  3249. /// // Create a custom property.
  3250. /// CustomProperty name = new CustomProperty("name", "Cathal Coffey");
  3251. ///
  3252. /// // Add this custom property to this document.
  3253. /// document.AddCustomProperty(name);
  3254. ///
  3255. /// // Create a text formatting.
  3256. /// Formatting f = new Formatting();
  3257. /// f.Bold = true;
  3258. /// f.Size = 14;
  3259. /// f.StrikeThrough = StrickThrough.strike;
  3260. ///
  3261. /// // Insert a new paragraph.
  3262. /// Paragraph p = document.InsertParagraph("Author: ", false, f);
  3263. ///
  3264. /// // Insert a field of type document property to display the custom property name and track this change.
  3265. /// p.InsertDocProperty(name, true, f);
  3266. ///
  3267. /// // Save all changes made to this document.
  3268. /// document.Save();
  3269. /// }// Release this document from memory.
  3270. /// </code>
  3271. /// </example>
  3272. public DocProperty InsertDocProperty(CustomProperty cp, bool trackChanges = false, Formatting f = null)
  3273. {
  3274. XElement f_xml = null;
  3275. if (f != null)
  3276. f_xml = f.Xml;
  3277. XElement e = new XElement
  3278. (
  3279. XName.Get("fldSimple", DocX.w.NamespaceName),
  3280. new XAttribute(XName.Get("instr", DocX.w.NamespaceName), string.Format(@"DOCPROPERTY {0} \* MERGEFORMAT", cp.Name)),
  3281. new XElement(XName.Get("r", DocX.w.NamespaceName),
  3282. new XElement(XName.Get("t", DocX.w.NamespaceName), f_xml, cp.Value))
  3283. );
  3284. XElement xml = e;
  3285. if (trackChanges)
  3286. {
  3287. DateTime now = DateTime.Now;
  3288. DateTime insert_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  3289. e = CreateEdit(EditType.ins, insert_datetime, e);
  3290. }
  3291. Xml.Add(e);
  3292. return new DocProperty(Document, xml);
  3293. }
  3294. /// <summary>
  3295. /// Removes characters from a Novacode.DocX.Paragraph.
  3296. /// </summary>
  3297. /// <example>
  3298. /// <code>
  3299. /// // Create a document using a relative filename.
  3300. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  3301. /// {
  3302. /// // Iterate through the paragraphs
  3303. /// foreach (Paragraph p in document.Paragraphs)
  3304. /// {
  3305. /// // Remove the first two characters from every paragraph
  3306. /// p.RemoveText(0, 2, false);
  3307. /// }
  3308. ///
  3309. /// // Save all changes made to this document.
  3310. /// document.Save();
  3311. /// }// Release this document from memory.
  3312. /// </code>
  3313. /// </example>
  3314. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  3315. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  3316. /// <param name="index">The position to begin deleting characters.</param>
  3317. /// <param name="count">The number of characters to delete</param>
  3318. /// <param name="trackChanges">Track changes</param>
  3319. /// <param name="removeEmptyParagraph">Remove empty paragraph</param>
  3320. public void RemoveText(int index, int count, bool trackChanges = false, bool removeEmptyParagraph=true)
  3321. {
  3322. // Timestamp to mark the start of insert
  3323. DateTime now = DateTime.Now;
  3324. DateTime remove_datetime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
  3325. // The number of characters processed so far
  3326. int processed = 0;
  3327. do
  3328. {
  3329. // Get the first run effected by this Remove
  3330. Run run = GetFirstRunEffectedByEdit(index, EditType.del);
  3331. // The parent of this Run
  3332. XElement parentElement = run.Xml.Parent;
  3333. switch (parentElement.Name.LocalName)
  3334. {
  3335. case "ins":
  3336. {
  3337. XElement[] splitEditBefore = SplitEdit(parentElement, index, EditType.del);
  3338. int min = Math.Min(count - processed, run.Xml.ElementsAfterSelf().Sum(e => GetElementTextLength(e)));
  3339. XElement[] splitEditAfter = SplitEdit(parentElement, index + min, EditType.del);
  3340. XElement temp = SplitEdit(splitEditBefore[1], index + min, EditType.del)[0];
  3341. object middle = CreateEdit(EditType.del, remove_datetime, temp.Elements());
  3342. processed += GetElementTextLength(middle as XElement);
  3343. if (!trackChanges)
  3344. middle = null;
  3345. parentElement.ReplaceWith
  3346. (
  3347. splitEditBefore[0],
  3348. middle,
  3349. splitEditAfter[1]
  3350. );
  3351. processed += GetElementTextLength(middle as XElement);
  3352. break;
  3353. }
  3354. case "del":
  3355. {
  3356. if (trackChanges)
  3357. {
  3358. // You cannot delete from a deletion, advance processed to the end of this del
  3359. processed += GetElementTextLength(parentElement);
  3360. }
  3361. else
  3362. goto case "ins";
  3363. break;
  3364. }
  3365. default:
  3366. {
  3367. XElement[] splitRunBefore = Run.SplitRun(run, index, EditType.del);
  3368. //int min = Math.Min(index + processed + (count - processed), run.EndIndex);
  3369. int min = Math.Min(index + (count - processed), run.EndIndex);
  3370. XElement[] splitRunAfter = Run.SplitRun(run, min, EditType.del);
  3371. 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] });
  3372. processed += GetElementTextLength(middle as XElement);
  3373. if (!trackChanges)
  3374. middle = null;
  3375. run.Xml.ReplaceWith
  3376. (
  3377. splitRunBefore[0],
  3378. middle,
  3379. splitRunAfter[1]
  3380. );
  3381. break;
  3382. }
  3383. }
  3384. // If after this remove the parent element is empty, remove it.
  3385. if (removeEmptyParagraph && GetElementTextLength(parentElement) == 0)
  3386. {
  3387. if (parentElement.Parent != null && parentElement.Parent.Name.LocalName != "tc")
  3388. {
  3389. // Need to make sure there is no drawing element within the parent element.
  3390. // Picture elements contain no text length but they are still content.
  3391. if (parentElement.Descendants(XName.Get("drawing", DocX.w.NamespaceName)).Count() == 0)
  3392. parentElement.Remove();
  3393. }
  3394. }
  3395. }
  3396. while (processed < count);
  3397. HelperFunctions.RenumberIDs(Document);
  3398. }
  3399. /// <summary>
  3400. /// Removes characters from a Novacode.DocX.Paragraph.
  3401. /// </summary>
  3402. /// <example>
  3403. /// <code>
  3404. /// // Create a document using a relative filename.
  3405. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  3406. /// {
  3407. /// // Iterate through the paragraphs
  3408. /// foreach (Paragraph p in document.Paragraphs)
  3409. /// {
  3410. /// // Remove all but the first 2 characters from this Paragraph.
  3411. /// p.RemoveText(2, false);
  3412. /// }
  3413. ///
  3414. /// // Save all changes made to this document.
  3415. /// document.Save();
  3416. /// }// Release this document from memory.
  3417. /// </code>
  3418. /// </example>
  3419. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  3420. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  3421. /// <param name="index">The position to begin deleting characters.</param>
  3422. /// <param name="trackChanges">Track changes</param>
  3423. public void RemoveText(int index, bool trackChanges = false)
  3424. {
  3425. RemoveText(index, Text.Length - index, trackChanges);
  3426. }
  3427. /// <summary>
  3428. /// Replaces all occurrences of a specified System.String in this instance, with another specified System.String.
  3429. /// </summary>
  3430. /// <example>
  3431. /// <code>
  3432. /// // Load a document using a relative filename.
  3433. /// using (DocX document = DocX.Load(@"C:\Example\Test.docx"))
  3434. /// {
  3435. /// // The formatting to match.
  3436. /// Formatting matchFormatting = new Formatting();
  3437. /// matchFormatting.Size = 10;
  3438. /// matchFormatting.Italic = true;
  3439. /// matchFormatting.FontFamily = new FontFamily("Times New Roman");
  3440. ///
  3441. /// // The formatting to apply to the inserted text.
  3442. /// Formatting newFormatting = new Formatting();
  3443. /// newFormatting.Size = 22;
  3444. /// newFormatting.UnderlineStyle = UnderlineStyle.dotted;
  3445. /// newFormatting.Bold = true;
  3446. ///
  3447. /// // Iterate through the paragraphs in this document.
  3448. /// foreach (Paragraph p in document.Paragraphs)
  3449. /// {
  3450. /// /*
  3451. /// * Replace all instances of the string "wrong" with the string "right" and ignore case.
  3452. /// * Each inserted instance of "wrong" should use the Formatting newFormatting.
  3453. /// * Only replace an instance of "wrong" if it is Size 10, Italic and Times New Roman.
  3454. /// * SubsetMatch means that the formatting must contain all elements of the match formatting,
  3455. /// * but it can also contain additional formatting for example Color, UnderlineStyle, etc.
  3456. /// * ExactMatch means it must not contain additional formatting.
  3457. /// */
  3458. /// p.ReplaceText("wrong", "right", false, RegexOptions.IgnoreCase, newFormatting, matchFormatting, MatchFormattingOptions.SubsetMatch);
  3459. /// }
  3460. ///
  3461. /// // Save all changes made to this document.
  3462. /// document.Save();
  3463. /// }// Release this document from memory.
  3464. /// </code>
  3465. /// </example>
  3466. /// <seealso cref="Paragraph.RemoveText(int, int, bool, bool)"/>
  3467. /// <seealso cref="Paragraph.RemoveText(int, bool)"/>
  3468. /// <seealso cref="Paragraph.InsertText(int, string, bool, Formatting)"/>
  3469. /// <seealso cref="Paragraph.InsertText(string, bool, Formatting)"/>
  3470. /// <param name="newValue">A System.String to replace all occurrences of oldValue.</param>
  3471. /// <param name="oldValue">A System.String to be replaced.</param>
  3472. /// <param name="options">A bitwise OR combination of RegexOption enumeration options.</param>
  3473. /// <param name="trackChanges">Track changes</param>
  3474. /// <param name="newFormatting">The formatting to apply to the text being inserted.</param>
  3475. /// <param name="matchFormatting">The formatting that the text must match in order to be replaced.</param>
  3476. /// <param name="fo">How should formatting be matched?</param>
  3477. /// <param name="escapeRegEx">True if the oldValue needs to be escaped, otherwise false. If it represents a valid RegEx pattern this should be false.</param>
  3478. /// <param name="useRegExSubstitutions">True if RegEx-like replace should be performed, i.e. if newValue contains RegEx substitutions. Does not perform named-group substitutions (only numbered groups).</param>
  3479. /// <param name="removeEmptyParagraph">Remove empty paragraph</param>
  3480. public void ReplaceText(string oldValue, string newValue, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions fo = MatchFormattingOptions.SubsetMatch, bool escapeRegEx = true, bool useRegExSubstitutions = false, bool removeEmptyParagraph = true)
  3481. {
  3482. string tText = Text;
  3483. MatchCollection mc = Regex.Matches(tText, escapeRegEx ? Regex.Escape(oldValue) : oldValue, options);
  3484. // Loop through the matches in reverse order
  3485. foreach (Match m in mc.Cast<Match>().Reverse())
  3486. {
  3487. // Assume the formatting matches until proven otherwise.
  3488. bool formattingMatch = true;
  3489. // Does the user want to match formatting?
  3490. if (matchFormatting != null)
  3491. {
  3492. // The number of characters processed so far
  3493. int processed = 0;
  3494. do
  3495. {
  3496. // Get the next run effected
  3497. Run run = GetFirstRunEffectedByEdit(m.Index + processed);
  3498. // Get this runs properties
  3499. XElement rPr = run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName));
  3500. if (rPr == null)
  3501. rPr = new Formatting().Xml;
  3502. /*
  3503. * Make sure that every formatting element in f.xml is also in this run,
  3504. * if this is not true, then their formatting does not match.
  3505. */
  3506. if (!HelperFunctions.ContainsEveryChildOf(matchFormatting.Xml, rPr, fo))
  3507. {
  3508. formattingMatch = false;
  3509. break;
  3510. }
  3511. // We have processed some characters, so update the counter.
  3512. processed += run.Value.Length;
  3513. } while (processed < m.Length);
  3514. }
  3515. // If the formatting matches, do the replace.
  3516. if (formattingMatch)
  3517. {
  3518. string repl = newValue;
  3519. //perform RegEx substitutions. Only named groups are not supported. Everything else is supported. However character escapes are not covered.
  3520. if (useRegExSubstitutions && !String.IsNullOrEmpty(repl))
  3521. {
  3522. repl = repl.Replace("$&", m.Value);
  3523. if (m.Groups.Count > 0)
  3524. {
  3525. int lastcap = 0;
  3526. for (int k = 0; k < m.Groups.Count; k++)
  3527. {
  3528. var g = m.Groups[k];
  3529. if ((g == null) || (g.Value == ""))
  3530. continue;
  3531. repl = repl.Replace("$" + k.ToString(), g.Value);
  3532. lastcap = k;
  3533. //cannot get named groups ATM
  3534. }
  3535. repl = repl.Replace("$+", m.Groups[lastcap].Value);
  3536. }
  3537. if (m.Index > 0)
  3538. {
  3539. repl = repl.Replace("$`", tText.Substring(0, m.Index));
  3540. }
  3541. if ((m.Index + m.Length) < tText.Length)
  3542. {
  3543. repl = repl.Replace("$'", tText.Substring(m.Index + m.Length));
  3544. }
  3545. repl = repl.Replace("$_", tText);
  3546. repl = repl.Replace("$$", "$");
  3547. }
  3548. if (!String.IsNullOrEmpty(repl))
  3549. InsertText(m.Index + m.Length, repl, trackChanges, newFormatting);
  3550. if (m.Length > 0)
  3551. RemoveText(m.Index, m.Length, trackChanges, removeEmptyParagraph);
  3552. }
  3553. }
  3554. }
  3555. /// <summary>
  3556. /// Find pattern regex must return a group match.
  3557. /// </summary>
  3558. /// <param name="findPattern">Regex pattern that must include one group match. ie (.*)</param>
  3559. /// <param name="regexMatchHandler">A func that accepts the matching find grouping text and returns a replacement value</param>
  3560. /// <param name="trackChanges"></param>
  3561. /// <param name="options"></param>
  3562. /// <param name="newFormatting"></param>
  3563. /// <param name="matchFormatting"></param>
  3564. /// <param name="fo"></param>
  3565. /// <param name="removeEmptyParagraph">Remove empty paragraph</param>
  3566. public void ReplaceText(string findPattern, Func<string, string> regexMatchHandler, bool trackChanges = false, RegexOptions options = RegexOptions.None, Formatting newFormatting = null, Formatting matchFormatting = null, MatchFormattingOptions fo = MatchFormattingOptions.SubsetMatch, bool removeEmptyParagraph = true)
  3567. {
  3568. var matchCollection = Regex.Matches(Text, findPattern, options);
  3569. // Loop through the matches in reverse order
  3570. foreach (var match in matchCollection.Cast<Match>().Reverse())
  3571. {
  3572. // Assume the formatting matches until proven otherwise.
  3573. bool formattingMatch = true;
  3574. // Does the user want to match formatting?
  3575. if (matchFormatting != null)
  3576. {
  3577. // The number of characters processed so far
  3578. int processed = 0;
  3579. do
  3580. {
  3581. // Get the next run effected
  3582. Run run = GetFirstRunEffectedByEdit(match.Index + processed);
  3583. // Get this runs properties
  3584. XElement rPr = run.Xml.Element(XName.Get("rPr", DocX.w.NamespaceName));
  3585. if (rPr == null)
  3586. rPr = new Formatting().Xml;
  3587. /*
  3588. * Make sure that every formatting element in f.xml is also in this run,
  3589. * if this is not true, then their formatting does not match.
  3590. */
  3591. if (!HelperFunctions.ContainsEveryChildOf(matchFormatting.Xml, rPr, fo))
  3592. {
  3593. formattingMatch = false;
  3594. break;
  3595. }
  3596. // We have processed some characters, so update the counter.
  3597. processed += run.Value.Length;
  3598. } while (processed < match.Length);
  3599. }
  3600. // If the formatting matches, do the replace.
  3601. if (formattingMatch)
  3602. {
  3603. var newValue = regexMatchHandler.Invoke(match.Groups[1].Value);
  3604. InsertText(match.Index + match.Value.Length, newValue, trackChanges, newFormatting);
  3605. RemoveText(match.Index, match.Value.Length, trackChanges, removeEmptyParagraph);
  3606. }
  3607. }
  3608. }
  3609. /// <summary>
  3610. /// Find all instances of a string in this paragraph and return their indexes in a List.
  3611. /// </summary>
  3612. /// <param name="str">The string to find</param>
  3613. /// <returns>A list of indexes.</returns>
  3614. /// <example>
  3615. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  3616. /// <code>
  3617. /// // Load a document
  3618. /// using (DocX document = DocX.Load(@"Test.docx"))
  3619. /// {
  3620. /// // Loop through the paragraphs in this document.
  3621. /// foreach(Paragraph p in document.Paragraphs)
  3622. /// {
  3623. /// // Find all instances of 'go' in this paragraph.
  3624. /// <![CDATA[ List<int> ]]> gos = document.FindAll("go");
  3625. ///
  3626. /// /*
  3627. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  3628. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  3629. /// * in document order, every insert would shift the index of the remaining matches.
  3630. /// */
  3631. /// gos.Reverse();
  3632. /// foreach (int index in gos)
  3633. /// {
  3634. /// p.InsertText(index, "don't ", false);
  3635. /// }
  3636. /// }
  3637. ///
  3638. /// // Save all changes made to this document.
  3639. /// document.Save();
  3640. /// }// Release this document from memory.
  3641. /// </code>
  3642. /// </example>
  3643. public List<int> FindAll(string str)
  3644. {
  3645. return FindAll(str, RegexOptions.None);
  3646. }
  3647. /// <summary>
  3648. /// Find all instances of a string in this paragraph and return their indexes in a List.
  3649. /// </summary>
  3650. /// <param name="str">The string to find</param>
  3651. /// <param name="options">The options to use when finding a string match.</param>
  3652. /// <returns>A list of indexes.</returns>
  3653. /// <example>
  3654. /// Find all instances of Hello in this document and insert 'don't' in frount of them.
  3655. /// <code>
  3656. /// // Load a document
  3657. /// using (DocX document = DocX.Load(@"Test.docx"))
  3658. /// {
  3659. /// // Loop through the paragraphs in this document.
  3660. /// foreach(Paragraph p in document.Paragraphs)
  3661. /// {
  3662. /// // Find all instances of 'go' in this paragraph (Ignore case).
  3663. /// <![CDATA[ List<int> ]]> gos = document.FindAll("go", RegexOptions.IgnoreCase);
  3664. ///
  3665. /// /*
  3666. /// * Insert 'don't' in frount of every instance of 'go' in this document to produce 'don't go'.
  3667. /// * An important trick here is to do the inserting in reverse document order. If you inserted
  3668. /// * in document order, every insert would shift the index of the remaining matches.
  3669. /// */
  3670. /// gos.Reverse();
  3671. /// foreach (int index in gos)
  3672. /// {
  3673. /// p.InsertText(index, "don't ", false);
  3674. /// }
  3675. /// }
  3676. ///
  3677. /// // Save all changes made to this document.
  3678. /// document.Save();
  3679. /// }// Release this document from memory.
  3680. /// </code>
  3681. /// </example>
  3682. public List<int> FindAll(string str, RegexOptions options)
  3683. {
  3684. MatchCollection mc = Regex.Matches(this.Text, Regex.Escape(str), options);
  3685. var query =
  3686. (
  3687. from m in mc.Cast<Match>()
  3688. select m.Index
  3689. ).ToList();
  3690. return query;
  3691. }
  3692. /// <summary>
  3693. /// Find all unique instances of the given Regex Pattern
  3694. /// </summary>
  3695. /// <param name="str"></param>
  3696. /// <param name="options"></param>
  3697. /// <returns></returns>
  3698. public List<string> FindAllByPattern(string str, RegexOptions options)
  3699. {
  3700. MatchCollection mc = Regex.Matches(this.Text, str, options);
  3701. var query =
  3702. (
  3703. from m in mc.Cast<Match>()
  3704. select m.Value
  3705. ).ToList();
  3706. return query;
  3707. }
  3708. /// <summary>
  3709. /// Insert a PageNumber place holder into a Paragraph.
  3710. /// This place holder should only be inserted into a Header or Footer Paragraph.
  3711. /// Word will not automatically update this field if it is inserted into a document level Paragraph.
  3712. /// </summary>
  3713. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3714. /// <param name="index">The text index to insert this PageNumber place holder at.</param>
  3715. /// <example>
  3716. /// <code>
  3717. /// // Create a new document.
  3718. /// using (DocX document = DocX.Create(@"Test.docx"))
  3719. /// {
  3720. /// // Add Headers to the document.
  3721. /// document.AddHeaders();
  3722. ///
  3723. /// // Get the default Header.
  3724. /// Header header = document.Headers.odd;
  3725. ///
  3726. /// // Insert a Paragraph into the Header.
  3727. /// Paragraph p0 = header.InsertParagraph("Page ( of )");
  3728. ///
  3729. /// // Insert place holders for PageNumber and PageCount into the Header.
  3730. /// // Word will replace these with the correct value for each Page.
  3731. /// p0.InsertPageNumber(PageNumberFormat.normal, 6);
  3732. /// p0.InsertPageCount(PageNumberFormat.normal, 11);
  3733. ///
  3734. /// // Save the document.
  3735. /// document.Save();
  3736. /// }
  3737. /// </code>
  3738. /// </example>
  3739. /// <seealso cref="AppendPageCount"/>
  3740. /// <seealso cref="AppendPageNumber"/>
  3741. /// <seealso cref="InsertPageCount"/>
  3742. public void InsertPageNumber(PageNumberFormat pnf, int index = 0)
  3743. {
  3744. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3745. if (pnf == PageNumberFormat.normal)
  3746. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* MERGEFORMAT "));
  3747. else
  3748. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* ROMAN \* MERGEFORMAT "));
  3749. XElement content = XElement.Parse
  3750. (
  3751. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3752. <w:rPr>
  3753. <w:noProof />
  3754. </w:rPr>
  3755. <w:t>1</w:t>
  3756. </w:r>"
  3757. );
  3758. fldSimple.Add(content);
  3759. if (index == 0)
  3760. Xml.AddFirst(fldSimple);
  3761. else
  3762. {
  3763. Run r = GetFirstRunEffectedByEdit(index, EditType.ins);
  3764. XElement[] splitEdit = SplitEdit(r.Xml, index, EditType.ins);
  3765. r.Xml.ReplaceWith
  3766. (
  3767. splitEdit[0],
  3768. fldSimple,
  3769. splitEdit[1]
  3770. );
  3771. }
  3772. }
  3773. /// <summary>
  3774. /// Append a PageNumber place holder onto the end of a Paragraph.
  3775. /// </summary>
  3776. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3777. /// <example>
  3778. /// <code>
  3779. /// // Create a new document.
  3780. /// using (DocX document = DocX.Create(@"Test.docx"))
  3781. /// {
  3782. /// // Add Headers to the document.
  3783. /// document.AddHeaders();
  3784. ///
  3785. /// // Get the default Header.
  3786. /// Header header = document.Headers.odd;
  3787. ///
  3788. /// // Insert a Paragraph into the Header.
  3789. /// Paragraph p0 = header.InsertParagraph();
  3790. ///
  3791. /// // Appemd place holders for PageNumber and PageCount into the Header.
  3792. /// // Word will replace these with the correct value for each Page.
  3793. /// p0.Append("Page (");
  3794. /// p0.AppendPageNumber(PageNumberFormat.normal);
  3795. /// p0.Append(" of ");
  3796. /// p0.AppendPageCount(PageNumberFormat.normal);
  3797. /// p0.Append(")");
  3798. ///
  3799. /// // Save the document.
  3800. /// document.Save();
  3801. /// }
  3802. /// </code>
  3803. /// </example>
  3804. /// <seealso cref="AppendPageCount"/>
  3805. /// <seealso cref="InsertPageNumber"/>
  3806. /// <seealso cref="InsertPageCount"/>
  3807. public void AppendPageNumber(PageNumberFormat pnf)
  3808. {
  3809. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3810. if (pnf == PageNumberFormat.normal)
  3811. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* MERGEFORMAT "));
  3812. else
  3813. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" PAGE \* ROMAN \* MERGEFORMAT "));
  3814. XElement content = XElement.Parse
  3815. (
  3816. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3817. <w:rPr>
  3818. <w:noProof />
  3819. </w:rPr>
  3820. <w:t>1</w:t>
  3821. </w:r>"
  3822. );
  3823. fldSimple.Add(content);
  3824. Xml.Add(fldSimple);
  3825. }
  3826. /// <summary>
  3827. /// Insert a PageCount place holder into a Paragraph.
  3828. /// This place holder should only be inserted into a Header or Footer Paragraph.
  3829. /// Word will not automatically update this field if it is inserted into a document level Paragraph.
  3830. /// </summary>
  3831. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3832. /// <param name="index">The text index to insert this PageCount place holder at.</param>
  3833. /// <example>
  3834. /// <code>
  3835. /// // Create a new document.
  3836. /// using (DocX document = DocX.Create(@"Test.docx"))
  3837. /// {
  3838. /// // Add Headers to the document.
  3839. /// document.AddHeaders();
  3840. ///
  3841. /// // Get the default Header.
  3842. /// Header header = document.Headers.odd;
  3843. ///
  3844. /// // Insert a Paragraph into the Header.
  3845. /// Paragraph p0 = header.InsertParagraph("Page ( of )");
  3846. ///
  3847. /// // Insert place holders for PageNumber and PageCount into the Header.
  3848. /// // Word will replace these with the correct value for each Page.
  3849. /// p0.InsertPageNumber(PageNumberFormat.normal, 6);
  3850. /// p0.InsertPageCount(PageNumberFormat.normal, 11);
  3851. ///
  3852. /// // Save the document.
  3853. /// document.Save();
  3854. /// }
  3855. /// </code>
  3856. /// </example>
  3857. /// <seealso cref="AppendPageCount"/>
  3858. /// <seealso cref="AppendPageNumber"/>
  3859. /// <seealso cref="InsertPageNumber"/>
  3860. public void InsertPageCount(PageNumberFormat pnf, int index = 0)
  3861. {
  3862. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3863. if (pnf == PageNumberFormat.normal)
  3864. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* MERGEFORMAT "));
  3865. else
  3866. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* ROMAN \* MERGEFORMAT "));
  3867. XElement content = XElement.Parse
  3868. (
  3869. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3870. <w:rPr>
  3871. <w:noProof />
  3872. </w:rPr>
  3873. <w:t>1</w:t>
  3874. </w:r>"
  3875. );
  3876. fldSimple.Add(content);
  3877. if (index == 0)
  3878. Xml.AddFirst(fldSimple);
  3879. else
  3880. {
  3881. Run r = GetFirstRunEffectedByEdit(index, EditType.ins);
  3882. XElement[] splitEdit = SplitEdit(r.Xml, index, EditType.ins);
  3883. r.Xml.ReplaceWith
  3884. (
  3885. splitEdit[0],
  3886. fldSimple,
  3887. splitEdit[1]
  3888. );
  3889. }
  3890. }
  3891. /// <summary>
  3892. /// Append a PageCount place holder onto the end of a Paragraph.
  3893. /// </summary>
  3894. /// <param name="pnf">The PageNumberFormat can be normal: (1, 2, ...) or Roman: (I, II, ...)</param>
  3895. /// <example>
  3896. /// <code>
  3897. /// // Create a new document.
  3898. /// using (DocX document = DocX.Create(@"Test.docx"))
  3899. /// {
  3900. /// // Add Headers to the document.
  3901. /// document.AddHeaders();
  3902. ///
  3903. /// // Get the default Header.
  3904. /// Header header = document.Headers.odd;
  3905. ///
  3906. /// // Insert a Paragraph into the Header.
  3907. /// Paragraph p0 = header.InsertParagraph();
  3908. ///
  3909. /// // Appemd place holders for PageNumber and PageCount into the Header.
  3910. /// // Word will replace these with the correct value for each Page.
  3911. /// p0.Append("Page (");
  3912. /// p0.AppendPageNumber(PageNumberFormat.normal);
  3913. /// p0.Append(" of ");
  3914. /// p0.AppendPageCount(PageNumberFormat.normal);
  3915. /// p0.Append(")");
  3916. ///
  3917. /// // Save the document.
  3918. /// document.Save();
  3919. /// }
  3920. /// </code>
  3921. /// </example>
  3922. /// <seealso cref="AppendPageNumber"/>
  3923. /// <seealso cref="InsertPageNumber"/>
  3924. /// <seealso cref="InsertPageCount"/>
  3925. public void AppendPageCount(PageNumberFormat pnf)
  3926. {
  3927. XElement fldSimple = new XElement(XName.Get("fldSimple", DocX.w.NamespaceName));
  3928. if (pnf == PageNumberFormat.normal)
  3929. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* MERGEFORMAT "));
  3930. else
  3931. fldSimple.Add(new XAttribute(XName.Get("instr", DocX.w.NamespaceName), @" NUMPAGES \* ROMAN \* MERGEFORMAT "));
  3932. XElement content = XElement.Parse
  3933. (
  3934. @"<w:r w:rsidR='001D0226' xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
  3935. <w:rPr>
  3936. <w:noProof />
  3937. </w:rPr>
  3938. <w:t>1</w:t>
  3939. </w:r>"
  3940. );
  3941. fldSimple.Add(content);
  3942. Xml.Add(fldSimple);
  3943. }
  3944. public float LineSpacingBefore
  3945. {
  3946. get
  3947. {
  3948. XElement pPr = GetOrCreate_pPr();
  3949. XElement spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3950. if (spacing != null)
  3951. {
  3952. XAttribute line = spacing.Attribute(XName.Get("before", DocX.w.NamespaceName));
  3953. if (line != null)
  3954. {
  3955. float f;
  3956. if (float.TryParse(line.Value, out f))
  3957. return f / 20.0f;
  3958. }
  3959. }
  3960. return 0.0f;
  3961. }
  3962. set
  3963. {
  3964. SpacingBefore(value);
  3965. }
  3966. }
  3967. public float LineSpacingAfter
  3968. {
  3969. get
  3970. {
  3971. XElement pPr = GetOrCreate_pPr();
  3972. XElement spacing = pPr.Element(XName.Get("spacing", DocX.w.NamespaceName));
  3973. if (spacing != null)
  3974. {
  3975. XAttribute line = spacing.Attribute(XName.Get("after", DocX.w.NamespaceName));
  3976. if (line != null)
  3977. {
  3978. float f;
  3979. if (float.TryParse(line.Value, out f))
  3980. return f / 20.0f;
  3981. }
  3982. }
  3983. return 10.0f;
  3984. }
  3985. set
  3986. {
  3987. SpacingAfter(value);
  3988. }
  3989. }
  3990. }
  3991. public class Run : DocXElement
  3992. {
  3993. // A lookup for the text elements in this paragraph
  3994. Dictionary<int, Text> textLookup = new Dictionary<int, Text>();
  3995. private int startIndex;
  3996. private int endIndex;
  3997. private string text;
  3998. /// <summary>
  3999. /// Gets the start index of this Text (text length before this text)
  4000. /// </summary>
  4001. public int StartIndex { get { return startIndex; } }
  4002. /// <summary>
  4003. /// Gets the end index of this Text (text length before this text + this texts length)
  4004. /// </summary>
  4005. public int EndIndex { get { return endIndex; } }
  4006. /// <summary>
  4007. /// The text value of this text element
  4008. /// </summary>
  4009. internal string Value { set { text = value; } get { return text; } }
  4010. internal Run(DocX document, XElement xml, int startIndex)
  4011. : base(document, xml)
  4012. {
  4013. this.startIndex = startIndex;
  4014. // Get the text elements in this run
  4015. IEnumerable<XElement> texts = xml.Descendants();
  4016. int start = startIndex;
  4017. // Loop through each text in this run
  4018. foreach (XElement te in texts)
  4019. {
  4020. switch (te.Name.LocalName)
  4021. {
  4022. case "tab":
  4023. {
  4024. textLookup.Add(start + 1, new Text(Document, te, start));
  4025. text += "\t";
  4026. start++;
  4027. break;
  4028. }
  4029. case "br":
  4030. {
  4031. textLookup.Add(start + 1, new Text(Document, te, start));
  4032. text += "\n";
  4033. start++;
  4034. break;
  4035. }
  4036. case "t": goto case "delText";
  4037. case "delText":
  4038. {
  4039. // Only add strings which are not empty
  4040. if (te.Value.Length > 0)
  4041. {
  4042. textLookup.Add(start + te.Value.Length, new Text(Document, te, start));
  4043. text += te.Value;
  4044. start += te.Value.Length;
  4045. }
  4046. break;
  4047. }
  4048. default: break;
  4049. }
  4050. }
  4051. endIndex = start;
  4052. }
  4053. static internal XElement[] SplitRun(Run r, int index, EditType type = EditType.ins)
  4054. {
  4055. index = index - r.StartIndex;
  4056. Text t = r.GetFirstTextEffectedByEdit(index, type);
  4057. XElement[] splitText = Text.SplitText(t, index);
  4058. 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]);
  4059. if (Paragraph.GetElementTextLength(splitLeft) == 0)
  4060. splitLeft = null;
  4061. 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"));
  4062. if (Paragraph.GetElementTextLength(splitRight) == 0)
  4063. splitRight = null;
  4064. return
  4065. (
  4066. new XElement[]
  4067. {
  4068. splitLeft,
  4069. splitRight
  4070. }
  4071. );
  4072. }
  4073. internal Text GetFirstTextEffectedByEdit(int index, EditType type = EditType.ins)
  4074. {
  4075. // Make sure we are looking within an acceptable index range.
  4076. if (index < 0 || index > HelperFunctions.GetText(Xml).Length)
  4077. throw new ArgumentOutOfRangeException();
  4078. // Need some memory that can be updated by the recursive search for the XElement to Split.
  4079. int count = 0;
  4080. Text theOne = null;
  4081. GetFirstTextEffectedByEditRecursive(Xml, index, ref count, ref theOne, type);
  4082. return theOne;
  4083. }
  4084. internal void GetFirstTextEffectedByEditRecursive(XElement Xml, int index, ref int count, ref Text theOne, EditType type = EditType.ins)
  4085. {
  4086. count += HelperFunctions.GetSize(Xml);
  4087. if (count > 0 && ((type == EditType.del && count > index) || (type == EditType.ins && count >= index)))
  4088. {
  4089. theOne = new Text(Document, Xml, count - HelperFunctions.GetSize(Xml));
  4090. return;
  4091. }
  4092. if (Xml.HasElements)
  4093. foreach (XElement e in Xml.Elements())
  4094. if (theOne == null)
  4095. GetFirstTextEffectedByEditRecursive(e, index, ref count, ref theOne);
  4096. }
  4097. }
  4098. internal class Text : DocXElement
  4099. {
  4100. private int startIndex;
  4101. private int endIndex;
  4102. private string text;
  4103. /// <summary>
  4104. /// Gets the start index of this Text (text length before this text)
  4105. /// </summary>
  4106. public int StartIndex { get { return startIndex; } }
  4107. /// <summary>
  4108. /// Gets the end index of this Text (text length before this text + this texts length)
  4109. /// </summary>
  4110. public int EndIndex { get { return endIndex; } }
  4111. /// <summary>
  4112. /// The text value of this text element
  4113. /// </summary>
  4114. public string Value { get { return text; } }
  4115. internal Text(DocX document, XElement xml, int startIndex)
  4116. : base(document, xml)
  4117. {
  4118. this.startIndex = startIndex;
  4119. switch (Xml.Name.LocalName)
  4120. {
  4121. case "t":
  4122. {
  4123. goto case "delText";
  4124. }
  4125. case "delText":
  4126. {
  4127. endIndex = startIndex + xml.Value.Length;
  4128. text = xml.Value;
  4129. break;
  4130. }
  4131. case "br":
  4132. {
  4133. text = "\n";
  4134. endIndex = startIndex + 1;
  4135. break;
  4136. }
  4137. case "tab":
  4138. {
  4139. text = "\t";
  4140. endIndex = startIndex + 1;
  4141. break;
  4142. }
  4143. default:
  4144. {
  4145. break;
  4146. }
  4147. }
  4148. }
  4149. internal static XElement[] SplitText(Text t, int index)
  4150. {
  4151. if (index < t.startIndex || index > t.EndIndex)
  4152. throw new ArgumentOutOfRangeException(nameof(index));
  4153. XElement splitLeft = null, splitRight = null;
  4154. if (t.Xml.Name.LocalName == "t" || t.Xml.Name.LocalName == "delText")
  4155. {
  4156. // The origional text element, now containing only the text before the index point.
  4157. splitLeft = new XElement(t.Xml.Name, t.Xml.Attributes(), t.Xml.Value.Substring(0, index - t.startIndex));
  4158. if (splitLeft.Value.Length == 0)
  4159. splitLeft = null;
  4160. else
  4161. PreserveSpace(splitLeft);
  4162. // The origional text element, now containing only the text after the index point.
  4163. splitRight = new XElement(t.Xml.Name, t.Xml.Attributes(), t.Xml.Value.Substring(index - t.startIndex, t.Xml.Value.Length - (index - t.startIndex)));
  4164. if (splitRight.Value.Length == 0)
  4165. splitRight = null;
  4166. else
  4167. PreserveSpace(splitRight);
  4168. }
  4169. else
  4170. {
  4171. if (index == t.EndIndex)
  4172. splitLeft = t.Xml;
  4173. else
  4174. splitRight = t.Xml;
  4175. }
  4176. return
  4177. (
  4178. new XElement[]
  4179. {
  4180. splitLeft,
  4181. splitRight
  4182. }
  4183. );
  4184. }
  4185. /// <summary>
  4186. /// If a text element or delText element, starts or ends with a space,
  4187. /// it must have the attribute space, otherwise it must not have it.
  4188. /// </summary>
  4189. /// <param name="e">The (t or delText) element check</param>
  4190. public static void PreserveSpace(XElement e)
  4191. {
  4192. // PreserveSpace should only be used on (t or delText) elements
  4193. if (!e.Name.Equals(DocX.w + "t") && !e.Name.Equals(DocX.w + "delText"))
  4194. throw new ArgumentException("SplitText can only split elements of type t or delText", "e");
  4195. // Check if this w:t contains a space atribute
  4196. XAttribute space = e.Attributes().Where(a => a.Name.Equals(XNamespace.Xml + "space")).SingleOrDefault();
  4197. // This w:t's text begins or ends with whitespace
  4198. if (e.Value.StartsWith(" ") || e.Value.EndsWith(" "))
  4199. {
  4200. // If this w:t contains no space attribute, add one.
  4201. if (space == null)
  4202. e.Add(new XAttribute(XNamespace.Xml + "space", "preserve"));
  4203. }
  4204. // This w:t's text does not begin or end with a space
  4205. else
  4206. {
  4207. // If this w:r contains a space attribute, remove it.
  4208. if (space != null)
  4209. space.Remove();
  4210. }
  4211. }
  4212. }
  4213. }