Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

Paragraph.cs 171KB

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