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 190KB

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