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

Paragraph.cs 164KB

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