RevisionProcessor.cs 134 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472
  1. // Copyright (c) Microsoft. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Linq;
  7. using System.Xml.Linq;
  8. using DocumentFormat.OpenXml.Packaging;
  9. namespace OpenXmlPowerTools
  10. {
  11. internal class ReverseRevisionsInfo
  12. {
  13. public bool InInsert;
  14. }
  15. public static class RevisionProcessor
  16. {
  17. public static WmlDocument RejectRevisions(WmlDocument document)
  18. {
  19. using (var streamDoc = new OpenXmlMemoryStreamDocument(document))
  20. {
  21. using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
  22. {
  23. RejectRevisions(doc);
  24. }
  25. return streamDoc.GetModifiedWmlDocument();
  26. }
  27. }
  28. public static void RejectRevisions(WordprocessingDocument doc)
  29. {
  30. // Reject revisions for those revisions that can't be rejected by inverting the sense
  31. // of the revision, and then accepting.
  32. RejectRevisionsForPart(doc.MainDocumentPart);
  33. foreach (HeaderPart part in doc.MainDocumentPart.HeaderParts)
  34. {
  35. RejectRevisionsForPart(part);
  36. }
  37. foreach (FooterPart part in doc.MainDocumentPart.FooterParts)
  38. {
  39. RejectRevisionsForPart(part);
  40. }
  41. if (doc.MainDocumentPart.EndnotesPart is EndnotesPart endnotesPart1)
  42. {
  43. RejectRevisionsForPart(endnotesPart1);
  44. }
  45. if (doc.MainDocumentPart.FootnotesPart is FootnotesPart footnotesPart1)
  46. {
  47. RejectRevisionsForPart(footnotesPart1);
  48. }
  49. if (doc.MainDocumentPart.StyleDefinitionsPart is StyleDefinitionsPart styleDefinitionsPart1)
  50. {
  51. RejectRevisionsForStylesDefinitionPart(styleDefinitionsPart1);
  52. }
  53. // Invert the sense of the revisions and accept those reverse revisions.
  54. ReverseRevisions(doc);
  55. AcceptRevisionsForPart(doc.MainDocumentPart);
  56. foreach (HeaderPart part in doc.MainDocumentPart.HeaderParts)
  57. {
  58. AcceptRevisionsForPart(part);
  59. }
  60. foreach (FooterPart part in doc.MainDocumentPart.FooterParts)
  61. {
  62. AcceptRevisionsForPart(part);
  63. }
  64. if (doc.MainDocumentPart.EndnotesPart is EndnotesPart endnotesPart2)
  65. {
  66. AcceptRevisionsForPart(endnotesPart2);
  67. }
  68. if (doc.MainDocumentPart.FootnotesPart is FootnotesPart footnotesPart2)
  69. {
  70. AcceptRevisionsForPart(footnotesPart2);
  71. }
  72. if (doc.MainDocumentPart.StyleDefinitionsPart is StyleDefinitionsPart styleDefinitionsPart2)
  73. {
  74. AcceptRevisionsForStylesDefinitionPart(styleDefinitionsPart2);
  75. }
  76. }
  77. // Reject revisions for those revisions that can't be rejected by inverting the sense of the revision, and then accepting.
  78. private static void RejectRevisionsForPart(OpenXmlPart part)
  79. {
  80. XElement root = part.GetXElement();
  81. object newRoot = RejectRevisionsForPartTransform(root);
  82. root.ReplaceWith(newRoot);
  83. part.PutXElement();
  84. }
  85. private static object RejectRevisionsForPartTransform(XNode node)
  86. {
  87. if (node is XElement element)
  88. {
  89. ////////////////////////////////////////////////////////////////////////////////////////
  90. // Inserted Numbering Properties
  91. #if false
  92. <w:p>
  93. <w:pPr>
  94. <w:pStyle w:val="ListParagraph"/>
  95. <w:numPr>
  96. <w:ilvl w:val="0"/>
  97. <w:numId w:val="1"/>
  98. <w:ins w:id="0" w:author="Eric White" w:date="2017-03-26T03:50:00Z" />
  99. </w:numPr>
  100. <w:rPr>
  101. <w:lang w:val="en-US"/>
  102. </w:rPr>
  103. </w:pPr>
  104. <w:r w:rsidRPr="009D59B3">
  105. <w:rPr>
  106. <w:lang w:val="en-US"/>
  107. </w:rPr>
  108. <w:t>This is a test.</w:t>
  109. </w:r>
  110. </w:p>
  111. #endif
  112. if (element.Name == W.numPr && element.Element(W.ins) != null)
  113. {
  114. return null;
  115. }
  116. ////////////////////////////////////////////////////////////////////////////////////////
  117. // Paragraph properties change
  118. #if false
  119. <w:p>
  120. <w:pPr>
  121. <w:pStyle w:val="ListParagraph"/>
  122. <w:numPr>
  123. <w:ilvl w:val="1"/>
  124. <w:numId w:val="2"/>
  125. </w:numPr>
  126. <w:rPr>
  127. <w:lang w:val="en-US"/>
  128. </w:rPr>
  129. <w:pPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T04:55:00Z">
  130. <w:pPr>
  131. <w:pStyle w:val="ListParagraph"/>
  132. <w:numPr>
  133. <w:ilvl w:val="1"/>
  134. <w:numId w:val="1"/>
  135. </w:numPr>
  136. <w:ind w:left="1440" w:hanging="360"/>
  137. </w:pPr>
  138. </w:pPrChange>
  139. </w:pPr>
  140. <w:r>
  141. <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
  142. </w:r>
  143. </w:p>
  144. #endif
  145. if (element.Name == W.pPr && element.Element(W.pPrChange) is XElement pPrChange)
  146. {
  147. XElement newPPr = pPrChange.Element(W.pPr) is XElement pPr
  148. ? new XElement(pPr)
  149. : new XElement(W.pPr);
  150. newPPr.Add(RejectRevisionsForPartTransform(element.Element(W.rPr)));
  151. return RejectRevisionsForPartTransform(newPPr);
  152. }
  153. ////////////////////////////////////////////////////////////////////////////////////////
  154. // Run properties change
  155. #if false
  156. <w:p w:rsidR="00615148" w:rsidRPr="00615148" w:rsidRDefault="00615148">
  157. <w:pPr>
  158. <w:rPr>
  159. <w:b/>
  160. <w:lang w:val="en-US"/>
  161. <w:rPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T05:02:00Z">
  162. <w:rPr>
  163. <w:lang w:val="en-US"/>
  164. </w:rPr>
  165. </w:rPrChange>
  166. </w:rPr>
  167. </w:pPr>
  168. <w:r>
  169. <w:rPr>
  170. <w:lang w:val="en-US"/>
  171. </w:rPr>
  172. <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
  173. </w:r>
  174. <w:bookmarkStart w:id="1" w:name="_GoBack"/>
  175. </w:p>
  176. #endif
  177. if (element.Name == W.rPr && element.Element(W.rPrChange) is XElement rPrChange)
  178. {
  179. XElement newRPr = rPrChange.Element(W.rPr);
  180. return RejectRevisionsForPartTransform(newRPr);
  181. }
  182. ////////////////////////////////////////////////////////////////////////////////////////
  183. // Field code numbering change
  184. #if false
  185. <w:p w:rsidR="00D46247" w:rsidRDefault="00D46247">
  186. <w:r>
  187. <w:fldChar w:fldCharType="begin"/>
  188. </w:r>
  189. <w:r>
  190. <w:instrText xml:space="preserve"> LISTNUM </w:instrText>
  191. </w:r>
  192. <w:r>
  193. <w:fldChar w:fldCharType="end">
  194. <w:numberingChange w:id="0" w:author="Eric White" w:date="2017-03-26T12:48:00Z" w:original="1)"/>
  195. </w:fldChar>
  196. </w:r>
  197. <w:r>
  198. <w:t xml:space="preserve"> Video provides a powerful way to help you prove your point.</w:t>
  199. </w:r>
  200. </w:p>
  201. #endif
  202. if (element.Name == W.numberingChange)
  203. {
  204. return null;
  205. }
  206. ////////////////////////////////////////////////////////////////////////////////////
  207. // Change w:sectPr
  208. #if false
  209. <w:p>
  210. <w:pPr>
  211. <w:rPr>
  212. <w:ins w:id="0" w:author="Eric White" w:date="2017-03-26T15:40:00Z"/>
  213. </w:rPr>
  214. <w:sectPr>
  215. <w:pgSz w:w="12240" w:h="15840"/>
  216. <w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720" w:header="720" w:footer="720" w:gutter="0"/>
  217. <w:cols w:space="720"/>
  218. <w:docGrid w:linePitch="360"/>
  219. <w:sectPrChange w:id="1" w:author="Eric White" w:date="2017-03-26T15:42:00Z">
  220. <w:sectPr w:rsidR="00620990" w:rsidSect="004E0757">
  221. <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
  222. </w:sectPr>
  223. </w:sectPrChange>
  224. </w:sectPr>
  225. </w:pPr>
  226. </w:p>
  227. #endif
  228. if (element.Name == W.sectPr && element.Element(W.sectPrChange) is XElement sectPrChange)
  229. {
  230. XElement newSectPr = sectPrChange.Element(W.sectPr);
  231. return RejectRevisionsForPartTransform(newSectPr);
  232. }
  233. ////////////////////////////////////////////////////////////////////////////////////
  234. // tblGridChange
  235. #if false
  236. <w:tblGrid>
  237. <w:gridCol w:w="1525"/>
  238. <w:gridCol w:w="3005"/>
  239. <w:gridCol w:w="3006"/>
  240. <w:tblGridChange w:id="1">
  241. <w:tblGrid>
  242. <w:gridCol w:w="3005"/>
  243. <w:gridCol w:w="3005"/>
  244. <w:gridCol w:w="3006"/>
  245. </w:tblGrid>
  246. </w:tblGridChange>
  247. </w:tblGrid>
  248. #endif
  249. if (element.Name == W.tblGrid && element.Element(W.tblGridChange) is XElement tblGridChange)
  250. {
  251. XElement newTblGrid = tblGridChange.Element(W.tblGrid);
  252. return RejectRevisionsForPartTransform(newTblGrid);
  253. }
  254. ////////////////////////////////////////////////////////////////////////////////////
  255. // tcPrChange
  256. #if false
  257. <w:tc>
  258. <w:tcPr>
  259. <w:tcW w:w="1525" w:type="dxa"/>
  260. <w:tcPrChange w:id="2" w:author="Eric White" w:date="2017-03-26T18:01:00Z">
  261. <w:tcPr>
  262. <w:tcW w:w="3005" w:type="dxa"/>
  263. </w:tcPr>
  264. </w:tcPrChange>
  265. </w:tcPr>
  266. <w:p>
  267. <w:r>
  268. <w:t>1</w:t>
  269. </w:r>
  270. </w:p>
  271. </w:tc>
  272. #endif
  273. if (element.Name == W.tcPr && element.Element(W.tcPrChange) is XElement tcPrChange)
  274. {
  275. XElement newTcPr = tcPrChange.Element(W.tcPr);
  276. return RejectRevisionsForPartTransform(newTcPr);
  277. }
  278. ////////////////////////////////////////////////////////////////////////////////////
  279. // trPrChange
  280. if (element.Name == W.trPr && element.Element(W.trPrChange) is XElement trPrChange)
  281. {
  282. XElement newTrPr = trPrChange.Element(W.trPr);
  283. return RejectRevisionsForPartTransform(newTrPr);
  284. }
  285. ////////////////////////////////////////////////////////////////////////////////////
  286. // tblPrExChange
  287. #if false
  288. <w:tblPrEx>
  289. <w:tblW w:w="0" w:type="auto"/>
  290. <w:tblPrExChange w:id="1" w:author="Eric White" w:date="2017-03-26T18:10:00Z">
  291. <w:tblPrEx>
  292. <w:tblW w:w="0" w:type="auto"/>
  293. </w:tblPrEx>
  294. </w:tblPrExChange>
  295. </w:tblPrEx>
  296. #endif
  297. #if false
  298. <w:tr w:rsidR="00097582" w:rsidTr="00F843C4">
  299. <w:tblPrEx>
  300. <w:tblW w:w="0" w:type="auto"/>
  301. <w:tblBorders>
  302. <w:top w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  303. <w:left w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  304. <w:bottom w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  305. <w:right w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  306. <w:insideH w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  307. <w:insideV w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  308. </w:tblBorders>
  309. <w:tblPrExChange w:id="1" w:author="Eric White" w:date="2017-03-26T20:38:00Z">
  310. <w:tblPrEx>
  311. <w:tblW w:w="0" w:type="auto"/>
  312. <w:tblBorders>
  313. <w:top w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  314. <w:left w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  315. <w:bottom w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  316. <w:right w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  317. <w:insideH w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  318. <w:insideV w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
  319. </w:tblBorders>
  320. </w:tblPrEx>
  321. </w:tblPrExChange>
  322. </w:tblPrEx>
  323. #endif
  324. if (element.Name == W.tblPrEx && element.Element(W.tblPrExChange) is XElement tblPrExChange)
  325. {
  326. XElement newTblPrEx = tblPrExChange.Element(W.tblPrEx);
  327. return RejectRevisionsForPartTransform(newTblPrEx);
  328. }
  329. ////////////////////////////////////////////////////////////////////////////////////
  330. // tblPrChange
  331. #if false
  332. <w:tbl>
  333. <w:tblPr>
  334. <w:tblStyle w:val="GridTable4-Accent1"/>
  335. <w:tblW w:w="0" w:type="auto"/>
  336. <w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
  337. <w:tblPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T20:05:00Z">
  338. <w:tblPr>
  339. <w:tblStyle w:val="TableGrid"/>
  340. <w:tblW w:w="0" w:type="auto"/>
  341. <w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand ="0" w:noVBand="1"/>
  342. </w:tblPr>
  343. </w:tblPrChange>
  344. </w:tblPr>
  345. #endif
  346. if (element.Name == W.tblPr && element.Element(W.tblPrChange) is XElement tblPrChange)
  347. {
  348. XElement newTrPr = tblPrChange.Element(W.tblPr);
  349. return RejectRevisionsForPartTransform(newTrPr);
  350. }
  351. ////////////////////////////////////////////////////////////////////////////////////
  352. // tblPrChange
  353. #if false
  354. <w:tc>
  355. <w:tcPr>
  356. <w:tcW w:w="3005" w:type="dxa"/>
  357. <w:cellDel w:id="8" w:author="Eric White" w:date="2017-03-26T21:12:00Z"/>
  358. <w:tcPrChange w:id="9" w:author="Eric White" w:date="2017-03-26T21:12:00Z">
  359. <w:tcPr>
  360. <w:tcW w:w="3005" w:type="dxa"/>
  361. <w:gridSpan w:val="2"/>
  362. <w:cellDel w:id="10" w:author="Eric White" w:date="2017-03-26T21:12:00Z"/>
  363. </w:tcPr>
  364. </w:tcPrChange>
  365. </w:tcPr>
  366. #endif
  367. if (element.Name == W.cellDel ||
  368. element.Name == W.cellMerge)
  369. {
  370. return null;
  371. }
  372. if (element.Name == W.tc &&
  373. element.Elements(W.tcPr).Elements(W.cellIns).Any())
  374. {
  375. return null;
  376. }
  377. return new XElement(element.Name,
  378. element.Attributes(),
  379. element.Nodes().Select(RejectRevisionsForPartTransform));
  380. }
  381. return node;
  382. }
  383. private static void RejectRevisionsForStylesDefinitionPart(StyleDefinitionsPart stylesDefinitionsPart)
  384. {
  385. XElement root = stylesDefinitionsPart.GetXElement();
  386. object newRoot = RejectRevisionsForStylesTransform(root);
  387. root.ReplaceWith(newRoot);
  388. stylesDefinitionsPart.PutXElement();
  389. }
  390. private static object RejectRevisionsForStylesTransform(XNode node)
  391. {
  392. if (node is XElement element)
  393. {
  394. if (element.Name == W.pPr && element.Element(W.pPrChange) is XElement pPrChange)
  395. {
  396. XElement newPPr = pPrChange.Element(W.pPr);
  397. return RejectRevisionsForStylesTransform(newPPr);
  398. }
  399. if (element.Name == W.rPr && element.Element(W.rPrChange) is XElement rPrChange)
  400. {
  401. XElement newRPr = rPrChange.Element(W.rPr);
  402. return RejectRevisionsForStylesTransform(newRPr);
  403. }
  404. return new XElement(element.Name,
  405. element.Attributes(),
  406. element.Nodes().Select(RejectRevisionsForStylesTransform));
  407. }
  408. return node;
  409. }
  410. private static void ReverseRevisions(WordprocessingDocument doc)
  411. {
  412. ReverseRevisionsForPart(doc.MainDocumentPart);
  413. foreach (HeaderPart part in doc.MainDocumentPart.HeaderParts)
  414. {
  415. ReverseRevisionsForPart(part);
  416. }
  417. foreach (FooterPart part in doc.MainDocumentPart.FooterParts)
  418. {
  419. ReverseRevisionsForPart(part);
  420. }
  421. if (doc.MainDocumentPart.EndnotesPart != null)
  422. {
  423. ReverseRevisionsForPart(doc.MainDocumentPart.EndnotesPart);
  424. }
  425. if (doc.MainDocumentPart.FootnotesPart != null)
  426. {
  427. ReverseRevisionsForPart(doc.MainDocumentPart.FootnotesPart);
  428. }
  429. }
  430. private static void ReverseRevisionsForPart(OpenXmlPart part)
  431. {
  432. XElement root = part.GetXElement();
  433. var rri = new ReverseRevisionsInfo { InInsert = false };
  434. var newRoot = (XElement)ReverseRevisionsTransform(root, rri);
  435. newRoot = (XElement)RemoveRsidTransform(newRoot);
  436. root.ReplaceWith(newRoot);
  437. part.PutXElement();
  438. }
  439. private static object RemoveRsidTransform(XNode node)
  440. {
  441. if (node is XElement element)
  442. {
  443. if (element.Name == W.rsid)
  444. return null;
  445. return new XElement(element.Name,
  446. element.Attributes().Where(a => a.Name != W.rsid &&
  447. a.Name != W.rsidDel &&
  448. a.Name != W.rsidP &&
  449. a.Name != W.rsidR &&
  450. a.Name != W.rsidRDefault &&
  451. a.Name != W.rsidRPr &&
  452. a.Name != W.rsidSect &&
  453. a.Name != W.rsidTr),
  454. element.Nodes().Select(RemoveRsidTransform));
  455. }
  456. return node;
  457. }
  458. private static object MergeAdjacentTablesTransform(XNode node)
  459. {
  460. if (node is XElement element)
  461. {
  462. if (element.Element(W.tbl) != null)
  463. {
  464. IEnumerable<IGrouping<string, XElement>> grouped = element
  465. .Elements()
  466. .GroupAdjacent(e =>
  467. {
  468. if (e.Name != W.tbl)
  469. return "";
  470. XElement bidiVisual = e.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
  471. string bidiVisString = bidiVisual == null ? "" : "|bidiVisual";
  472. string key = "tbl" + bidiVisString;
  473. return key;
  474. });
  475. IEnumerable<object> newContent = grouped
  476. .Select(g =>
  477. {
  478. if (g.Key == "" || g.Count() == 1)
  479. return (object)g;
  480. int[] rolled = g
  481. .Select(tbl =>
  482. {
  483. IEnumerable<int> gridCols = tbl
  484. .Elements(W.tblGrid)
  485. .Elements(W.gridCol)
  486. .Attributes(W._w)
  487. .Select(a => (int)a)
  488. .Rollup(0, (s, i) => s + i);
  489. return gridCols;
  490. })
  491. .SelectMany(m => m)
  492. .Distinct()
  493. .OrderBy(w => w)
  494. .ToArray();
  495. var newTable = new XElement(W.tbl,
  496. g.First().Elements(W.tblPr),
  497. new XElement(W.tblGrid,
  498. rolled.Select((r, i) =>
  499. {
  500. int v;
  501. if (i == 0)
  502. v = r;
  503. else
  504. v = r - rolled[i - 1];
  505. return new XElement(W.gridCol,
  506. new XAttribute(W._w, v));
  507. })),
  508. g.Select(tbl =>
  509. {
  510. XElement fixedWidthsTbl = FixWidths(tbl);
  511. IEnumerable<XElement> newRows = fixedWidthsTbl.Elements(W.tr)
  512. .Select(tr =>
  513. {
  514. var newRow = new XElement(W.tr,
  515. tr.Attributes(),
  516. tr.Elements().Where(e => e.Name != W.tc),
  517. tr.Elements(W.tc).Select(tc =>
  518. {
  519. var w = (int?)tc
  520. .Elements(W.tcPr)
  521. .Elements(W.tcW)
  522. .Attributes(W._w)
  523. .FirstOrDefault();
  524. if (w == null)
  525. {
  526. return tc;
  527. }
  528. IEnumerable<XElement> cellsToLeft = tc
  529. .GetParent()
  530. .Elements(W.tc)
  531. .TakeWhile(btc => btc != tc)
  532. .ToList();
  533. var widthToLeft = 0;
  534. if (cellsToLeft.Any())
  535. {
  536. widthToLeft = cellsToLeft
  537. .Elements(W.tcPr)
  538. .Elements(W.tcW)
  539. .Attributes(W._w)
  540. .Select(wi => (int)wi)
  541. .Sum();
  542. }
  543. var rolledPairs = new[]
  544. {
  545. new
  546. {
  547. GridValue = 0,
  548. Index = 0
  549. }
  550. }
  551. .Concat(rolled
  552. .Select((r, i) => new
  553. {
  554. GridValue = r,
  555. Index = i + 1
  556. }))
  557. .ToList();
  558. var start = rolledPairs
  559. .FirstOrDefault(t => t.GridValue >= widthToLeft);
  560. if (start != null)
  561. {
  562. int gridsRequired = rolledPairs
  563. .Skip(start.Index)
  564. .TakeWhile(rp => rp.GridValue - start.GridValue < w)
  565. .Count();
  566. var tcPr = new XElement(W.tcPr,
  567. tc.Elements(W.tcPr).Elements().Where(e => e.Name != W.gridSpan),
  568. gridsRequired != 1
  569. ? new XElement(W.gridSpan,
  570. new XAttribute(W.val, gridsRequired))
  571. : null);
  572. var orderedTcPr = new XElement(W.tcPr,
  573. tcPr.Elements().OrderBy(e =>
  574. OrderTcPr.ContainsKey(e.Name) ? OrderTcPr[e.Name] : 999));
  575. var newCell = new XElement(W.tc,
  576. orderedTcPr,
  577. tc.Elements().Where(e => e.Name != W.tcPr));
  578. return newCell;
  579. }
  580. return tc;
  581. }));
  582. return newRow;
  583. });
  584. return newRows;
  585. }));
  586. return newTable;
  587. });
  588. return new XElement(element.Name,
  589. element.Attributes(),
  590. newContent);
  591. }
  592. return new XElement(element.Name,
  593. element.Attributes(),
  594. element.Nodes().Select(MergeAdjacentTablesTransform));
  595. }
  596. return node;
  597. }
  598. private static object ReverseRevisionsTransform(XNode node, ReverseRevisionsInfo rri)
  599. {
  600. if (node is XElement element)
  601. {
  602. XElement parent = element
  603. .Ancestors()
  604. .FirstOrDefault(a => a.Name != W.sdtContent &&
  605. a.Name != W.sdt &&
  606. a.Name != W.hyperlink &&
  607. a.Name != W.smartTag);
  608. ////////////////////////////////////////////////////////////////////////////////////
  609. // Deleted run
  610. #if false
  611. <w:p>
  612. <w:r>
  613. <w:t xml:space="preserve">Video </w:t>
  614. </w:r>
  615. <w:del>
  616. <w:r>
  617. <w:delText xml:space="preserve">provides </w:delText>
  618. </w:r>
  619. </w:del>
  620. <w:r>
  621. <w:t>a powerful way to help you prove your point.</w:t>
  622. </w:r>
  623. </w:p>
  624. #endif
  625. if (element.Name == W.del &&
  626. parent?.Name == W.p)
  627. {
  628. return new XElement(W.ins,
  629. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  630. }
  631. ////////////////////////////////////////////////////////////////////////////////////
  632. // Deleted paragraph mark
  633. #if false
  634. <w:p>
  635. <w:pPr>
  636. <w:rPr>
  637. <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T21:52:00Z"/>
  638. </w:rPr>
  639. </w:pPr>
  640. <w:r>
  641. <w:t>Video provides a powerful way to help you prove your point.</w:t>
  642. </w:r>
  643. </w:p>
  644. <w:p>
  645. <w:r>
  646. <w:t>You can also type a keyword to search online for the video that best fits your document.</w:t>
  647. </w:r>
  648. </w:p>
  649. #endif
  650. if (element.Name == W.del &&
  651. parent?.Name == W.rPr &&
  652. parent?.Parent?.Name == W.pPr)
  653. {
  654. return new XElement(W.ins);
  655. }
  656. ////////////////////////////////////////////////////////////////////////////////////
  657. // Inserted paragraph mark
  658. #if false
  659. <w:p>
  660. <w:pPr>
  661. <w:rPr>
  662. <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T21:58:00Z"/>
  663. </w:rPr>
  664. </w:pPr>
  665. <w:r>
  666. <w:t xml:space="preserve">Video provides a powerful way to help you prove your point. </w:t>
  667. </w:r>
  668. </w:p>
  669. <w:p>
  670. <w:r>
  671. <w:rPr>
  672. <w:lang w:val="en-US"/>
  673. </w:rPr>
  674. <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
  675. </w:r>
  676. </w:p>
  677. #endif
  678. if (element.Name == W.ins &&
  679. parent?.Name == W.rPr &&
  680. parent?.Parent?.Name == W.pPr)
  681. {
  682. return new XElement(W.del);
  683. }
  684. ////////////////////////////////////////////////////////////////////////////////////
  685. // Inserted run
  686. #if false
  687. <w:p>
  688. <w:r>
  689. <w:t xml:space="preserve">Video </w:t>
  690. </w:r>
  691. <w:ins>
  692. <w:r>
  693. <w:t xml:space="preserve">provides </w:t>
  694. </w:r>
  695. </w:ins>
  696. <w:r>
  697. <w:t>a powerful way to help you prove your point.</w:t>
  698. </w:r>
  699. </w:p>
  700. #endif
  701. if (element.Name == W.ins &&
  702. parent?.Name == W.p)
  703. {
  704. // TODO: Revisit. Why is newRri not used?
  705. var newRri = new ReverseRevisionsInfo { InInsert = true };
  706. return new XElement(W.del,
  707. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  708. }
  709. ////////////////////////////////////////////////////////////////////////////////////
  710. // Deleted table row
  711. #if false
  712. <w:tbl>
  713. <w:tr>
  714. <w:tc>
  715. <w:p>
  716. <w:r>
  717. <w:t>1</w:t>
  718. </w:r>
  719. </w:p>
  720. </w:tc>
  721. </w:tr>
  722. <w:tr>
  723. <w:trPr>
  724. <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T22:15:00Z"/>
  725. </w:trPr>
  726. <w:tc>
  727. <w:p>
  728. <w:pPr>
  729. <w:rPr>
  730. <w:del w:id="1" w:author="Eric White" w:date="2017-03-24T22:15:00Z"/>
  731. </w:rPr>
  732. </w:pPr>
  733. <w:del w:id="2" w:author="Eric White" w:date="2017-03-24T22:15:00Z">
  734. <w:r>
  735. <w:delText>4</w:delText>
  736. </w:r>
  737. </w:del>
  738. </w:p>
  739. </w:tc>
  740. </w:tr>
  741. <w:tr>
  742. <w:tc>
  743. <w:p>
  744. <w:r>
  745. <w:t>7</w:t>
  746. </w:r>
  747. </w:p>
  748. </w:tc>
  749. </w:tr>
  750. </w:tbl>
  751. #endif
  752. if (element.Name == W.del &&
  753. parent?.Name == W.trPr)
  754. {
  755. return new XElement(W.ins);
  756. }
  757. ////////////////////////////////////////////////////////////////////////////////////
  758. // Inserted table row
  759. #if false
  760. <w:tbl>
  761. <w:tr>
  762. <w:tc>
  763. <w:p>
  764. <w:r>
  765. <w:t>1</w:t>
  766. </w:r>
  767. </w:p>
  768. </w:tc>
  769. </w:tr>
  770. <w:tr>
  771. <w:trPr>
  772. <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T22:16:00Z"/>
  773. </w:trPr>
  774. <w:tc>
  775. <w:p>
  776. <w:pPr>
  777. <w:rPr>
  778. <w:ins w:id="1" w:author="Eric White" w:date="2017-03-24T22:16:00Z"/>
  779. </w:rPr>
  780. </w:pPr>
  781. <w:ins w:id="2" w:author="Eric White" w:date="2017-03-24T22:16:00Z">
  782. <w:r>
  783. <w:t>1a</w:t>
  784. </w:r>
  785. </w:ins>
  786. </w:p>
  787. </w:tc>
  788. </w:tr>
  789. <w:tr>
  790. <w:tc>
  791. <w:p>
  792. <w:r>
  793. <w:t>4</w:t>
  794. </w:r>
  795. </w:p>
  796. </w:tc>
  797. </w:tr>
  798. </w:tbl>
  799. #endif
  800. if (element.Name == W.ins &&
  801. parent?.Name == W.trPr)
  802. {
  803. return new XElement(W.del);
  804. }
  805. ////////////////////////////////////////////////////////////////////////////////////
  806. // Deleted math control character
  807. #if false
  808. <w:p w:rsidR="007F4E48" w:rsidRDefault="00C9403B">
  809. <m:oMathPara>
  810. <m:oMath>
  811. <m:r>
  812. <w:rPr>
  813. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  814. </w:rPr>
  815. <m:t>A=</m:t>
  816. </m:r>
  817. <m:r>
  818. <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T22:53:00Z">
  819. <w:rPr>
  820. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  821. </w:rPr>
  822. <m:t>2</m:t>
  823. </w:del>
  824. </m:r>
  825. <m:r>
  826. <w:rPr>
  827. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  828. </w:rPr>
  829. <m:t>π</m:t>
  830. </m:r>
  831. #endif
  832. if (element.Name == W.del &&
  833. parent?.Name == M.r)
  834. {
  835. return new XElement(W.ins,
  836. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  837. }
  838. ////////////////////////////////////////////////////////////////////////////////////
  839. // Inserted math control character
  840. #if false
  841. <w:p w:rsidR="007F4E48" w:rsidRDefault="00C9403B">
  842. <m:oMathPara>
  843. <m:oMath>
  844. <m:r>
  845. <w:rPr>
  846. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  847. </w:rPr>
  848. <m:t>A=</m:t>
  849. </m:r>
  850. <m:r>
  851. <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T22:54:00Z">
  852. <w:rPr>
  853. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  854. </w:rPr>
  855. <m:t>2</m:t>
  856. </w:ins>
  857. </m:r>
  858. <m:r>
  859. <w:rPr>
  860. <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
  861. </w:rPr>
  862. <m:t>π</m:t>
  863. </m:r>
  864. #endif
  865. if (element.Name == W.ins &&
  866. parent?.Name == M.r)
  867. {
  868. return new XElement(W.del,
  869. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  870. }
  871. ////////////////////////////////////////////////////////////////////////////////////
  872. // moveFrom / moveTo
  873. #if false
  874. <w:p>
  875. <w:r>
  876. <w:t>Video provides a powerful way.</w:t>
  877. </w:r>
  878. </w:p>
  879. <w:p>
  880. <w:pPr>
  881. <w:rPr>
  882. <w:moveFrom w:id="0" w:author="Eric White" w:date="2017-03-24T23:18:00Z"/>
  883. </w:rPr>
  884. </w:pPr>
  885. <w:moveFromRangeStart w:id="1" w:author="Eric White" w:date="2017-03-24T23:18:00Z" w:name="move478160808"/>
  886. <w:moveFrom w:id="2" w:author="Eric White" w:date="2017-03-24T23:18:00Z">
  887. <w:r>
  888. <w:t>When you click Online Video.</w:t>
  889. </w:r>
  890. </w:moveFrom>
  891. </w:p>
  892. <w:moveFromRangeEnd w:id="1"/>
  893. <w:p>
  894. <w:r>
  895. <w:rPr>
  896. <w:lang w:val="en-US"/>
  897. </w:rPr>
  898. <w:t>You can also type a keyword.</w:t>
  899. </w:r>
  900. </w:p>
  901. <w:p>
  902. <w:pPr>
  903. <w:rPr>
  904. <w:moveTo w:id="3" w:author="Eric White" w:date="2017-03-24T23:18:00Z"/>
  905. </w:rPr>
  906. </w:pPr>
  907. <w:moveToRangeStart w:id="5" w:author="Eric White" w:date="2017-03-24T23:18:00Z" w:name="move478160808"/>
  908. <w:moveTo w:id="6" w:author="Eric White" w:date="2017-03-24T23:18:00Z">
  909. <w:r>
  910. <w:t>When you click Online Video.</w:t>
  911. </w:r>
  912. </w:moveTo>
  913. </w:p>
  914. <w:moveToRangeEnd w:id="5"/>
  915. <w:p>
  916. <w:r>
  917. <w:t>Make your document look professionally produced.</w:t>
  918. </w:r>
  919. </w:p>
  920. #endif
  921. if (element.Name == W.moveFrom)
  922. {
  923. return new XElement(W.moveTo,
  924. element.Attributes(),
  925. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  926. }
  927. if (element.Name == W.moveFromRangeStart)
  928. {
  929. return new XElement(W.moveToRangeStart,
  930. element.Attributes(),
  931. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  932. }
  933. if (element.Name == W.moveFromRangeEnd)
  934. {
  935. return new XElement(W.moveToRangeEnd,
  936. element.Attributes(),
  937. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  938. }
  939. if (element.Name == W.moveTo)
  940. {
  941. return new XElement(W.moveFrom,
  942. element.Attributes(),
  943. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  944. }
  945. if (element.Name == W.moveToRangeStart)
  946. {
  947. return new XElement(W.moveFromRangeStart,
  948. element.Attributes(),
  949. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  950. }
  951. if (element.Name == W.moveToRangeEnd)
  952. {
  953. return new XElement(W.moveFromRangeEnd,
  954. element.Attributes(),
  955. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  956. }
  957. ////////////////////////////////////////////////////////////////////////////////////
  958. // Deleted content control
  959. #if false
  960. <w:p>
  961. <w:customXmlDelRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
  962. <w:sdt>
  963. <w:sdtPr>
  964. <w:rPr>
  965. <w:lang w:val="en-US"/>
  966. </w:rPr>
  967. <w:id w:val="990292373"/>
  968. <w:placeholder>
  969. <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
  970. </w:placeholder>
  971. <w:text/>
  972. </w:sdtPr>
  973. <w:sdtContent>
  974. <w:customXmlDelRangeEnd w:id="1"/>
  975. <w:r>
  976. <w:t>Video</w:t>
  977. </w:r>
  978. <w:customXmlDelRangeStart w:id="2" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
  979. </w:sdtContent>
  980. </w:sdt>
  981. <w:customXmlDelRangeEnd w:id="2"/>
  982. <w:r>
  983. <w:t xml:space="preserve"> provides a powerful way to help you prove your point.</w:t>
  984. </w:r>
  985. </w:p>
  986. #endif
  987. if (element.Name == W.customXmlDelRangeStart)
  988. {
  989. return new XElement(W.customXmlInsRangeStart,
  990. element.Attributes(),
  991. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  992. }
  993. if (element.Name == W.customXmlDelRangeEnd)
  994. {
  995. return new XElement(W.customXmlInsRangeEnd,
  996. element.Attributes(),
  997. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  998. }
  999. ////////////////////////////////////////////////////////////////////////////////////
  1000. // Inserted content control
  1001. #if false
  1002. <w:p>
  1003. <w:customXmlInsRangeStart w:id="0" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
  1004. <w:sdt>
  1005. <w:sdtPr>
  1006. <w:id w:val="-473839966"/>
  1007. <w:placeholder>
  1008. <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
  1009. </w:placeholder>
  1010. <w:text/>
  1011. </w:sdtPr>
  1012. <w:sdtContent>
  1013. <w:customXmlInsRangeEnd w:id="0"/>
  1014. <w:r>
  1015. <w:t>Video</w:t>
  1016. </w:r>
  1017. <w:customXmlInsRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
  1018. </w:sdtContent>
  1019. </w:sdt>
  1020. <w:customXmlInsRangeEnd w:id="1"/>
  1021. <w:r>
  1022. <w:rPr>
  1023. <w:lang w:val="en-US"/>
  1024. </w:rPr>
  1025. <w:t xml:space="preserve"> provides a powerful way to help you prove your point.</w:t>
  1026. </w:r>
  1027. </w:p>
  1028. #endif
  1029. if (element.Name == W.customXmlInsRangeStart)
  1030. {
  1031. return new XElement(W.customXmlDelRangeStart,
  1032. element.Attributes(),
  1033. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1034. }
  1035. if (element.Name == W.customXmlInsRangeEnd)
  1036. {
  1037. return new XElement(W.customXmlDelRangeEnd,
  1038. element.Attributes(),
  1039. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1040. }
  1041. ////////////////////////////////////////////////////////////////////////////////////
  1042. // Moved content control
  1043. #if false
  1044. <w:p>
  1045. <w:r>
  1046. <w:t>Video provides a powerful way.</w:t>
  1047. </w:r>
  1048. </w:p>
  1049. <w:customXmlMoveFromRangeStart w:id="0" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1050. <w:moveFromRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:21:00Z" w:name="move478243824" w:displacedByCustomXml="next"/>
  1051. <w:sdt>
  1052. <w:sdtPr>
  1053. <w:id w:val="-2060007328"/>
  1054. <w:placeholder>
  1055. <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
  1056. </w:placeholder>
  1057. </w:sdtPr>
  1058. <w:sdtContent>
  1059. <w:customXmlMoveFromRangeEnd w:id="0"/>
  1060. <w:p w:rsidR="00D306FD" w:rsidDel="001037E6" w:rsidRDefault="00D306FD">
  1061. <w:pPr>
  1062. <w:rPr>
  1063. <w:moveFrom w:id="2" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1064. <w:lang w:val="en-US"/>
  1065. </w:rPr>
  1066. </w:pPr>
  1067. <w:moveFrom w:id="3" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
  1068. <w:r w:rsidDel="001037E6">
  1069. <w:rPr>
  1070. <w:lang w:val="en-US"/>
  1071. </w:rPr>
  1072. <w:t>When you click Online Video.</w:t>
  1073. </w:r>
  1074. </w:moveFrom>
  1075. </w:p>
  1076. <w:customXmlMoveFromRangeStart w:id="4" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1077. </w:sdtContent>
  1078. </w:sdt>
  1079. <w:customXmlMoveFromRangeEnd w:id="4"/>
  1080. <w:moveFromRangeEnd w:id="1"/>
  1081. <w:p>
  1082. <w:r>
  1083. <w:rPr>
  1084. <w:lang w:val="en-US"/>
  1085. </w:rPr>
  1086. <w:t>You can also type a keyword.</w:t>
  1087. </w:r>
  1088. </w:p>
  1089. <w:p>
  1090. <w:r>
  1091. <w:rPr>
  1092. <w:lang w:val="en-US"/>
  1093. </w:rPr>
  1094. <w:t>To make your document look.</w:t>
  1095. </w:r>
  1096. </w:p>
  1097. <w:customXmlMoveToRangeStart w:id="5" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1098. <w:moveToRangeStart w:id="6" w:author="Eric White" w:date="2017-03-25T22:21:00Z" w:name="move478243824" w:displacedByCustomXml="next"/>
  1099. <w:sdt>
  1100. <w:sdtPr>
  1101. <w:id w:val="-483622649"/>
  1102. <w:placeholder>
  1103. <w:docPart w:val="DC46F197491D4EC8B79DB4CE2D22E222"/>
  1104. </w:placeholder>
  1105. </w:sdtPr>
  1106. <w:sdtContent>
  1107. <w:customXmlMoveToRangeEnd w:id="5"/>
  1108. <w:p>
  1109. <w:pPr>
  1110. <w:rPr>
  1111. <w:moveTo w:id="8" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1112. </w:rPr>
  1113. </w:pPr>
  1114. <w:moveTo w:id="9" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
  1115. <w:r>
  1116. <w:t>When you click Online Video.</w:t>
  1117. </w:r>
  1118. </w:moveTo>
  1119. </w:p>
  1120. <w:customXmlMoveToRangeStart w:id="10" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
  1121. </w:sdtContent>
  1122. </w:sdt>
  1123. <w:customXmlMoveToRangeEnd w:id="10"/>
  1124. <w:moveToRangeEnd w:id="6"/>
  1125. <w:p>
  1126. <w:ins w:id="11" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
  1127. <w:r>
  1128. <w:t xml:space="preserve"> </w:t>
  1129. </w:r>
  1130. </w:ins>
  1131. <w:r>
  1132. <w:t>For example, you can add.</w:t>
  1133. </w:r>
  1134. </w:p>
  1135. #endif
  1136. if (element.Name == W.customXmlMoveFromRangeStart)
  1137. {
  1138. return new XElement(W.customXmlMoveToRangeStart,
  1139. element.Attributes(),
  1140. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1141. }
  1142. if (element.Name == W.customXmlMoveFromRangeEnd)
  1143. {
  1144. return new XElement(W.customXmlMoveToRangeEnd,
  1145. element.Attributes(),
  1146. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1147. }
  1148. if (element.Name == W.customXmlMoveToRangeStart)
  1149. {
  1150. return new XElement(W.customXmlMoveFromRangeStart,
  1151. element.Attributes(),
  1152. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1153. }
  1154. if (element.Name == W.customXmlMoveToRangeEnd)
  1155. {
  1156. return new XElement(W.customXmlMoveFromRangeEnd,
  1157. element.Attributes(),
  1158. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1159. }
  1160. ////////////////////////////////////////////////////////////////////////////////////
  1161. // Deleted field code
  1162. #if false
  1163. <w:p>
  1164. <w:pPr>
  1165. <w:rPr>
  1166. <w:del w:id="0" w:author="Eric White" w:date="2017-03-25T22:43:00Z"/>
  1167. </w:rPr>
  1168. </w:pPr>
  1169. <w:del w:id="1" w:author="Eric White" w:date="2017-03-25T22:43:00Z">
  1170. <w:r>
  1171. <w:fldChar w:fldCharType="begin"/>
  1172. </w:r>
  1173. <w:r>
  1174. <w:delInstrText xml:space="preserve"> D</w:delInstrText>
  1175. </w:r>
  1176. <w:r>
  1177. <w:rPr>
  1178. <w:color w:val="FF0000"/>
  1179. </w:rPr>
  1180. <w:delInstrText>A</w:delInstrText>
  1181. </w:r>
  1182. <w:r>
  1183. <w:delInstrText xml:space="preserve">TE </w:delInstrText>
  1184. </w:r>
  1185. <w:r>
  1186. <w:fldChar w:fldCharType="separate"/>
  1187. </w:r>
  1188. <w:r>
  1189. <w:delText>25/03/2017</w:delText>
  1190. </w:r>
  1191. <w:r>
  1192. <w:fldChar w:fldCharType="end"/>
  1193. </w:r>
  1194. </w:del>
  1195. </w:p>
  1196. #endif
  1197. if (element.Name == W.delInstrText)
  1198. {
  1199. return new XElement(W.instrText,
  1200. element.Attributes(), // pulls in xml:space attribute
  1201. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1202. }
  1203. ////////////////////////////////////////////////////////////////////////////////////
  1204. // Change inserted instrText element to w:delInstrText
  1205. if (element.Name == W.instrText && rri.InInsert)
  1206. {
  1207. return new XElement(W.delInstrText,
  1208. element.Attributes(),
  1209. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1210. }
  1211. ////////////////////////////////////////////////////////////////////////////////////
  1212. // Change inserted text element to w:delText
  1213. if (element.Name == W.t && rri.InInsert)
  1214. {
  1215. return new XElement(W.delText,
  1216. element.Attributes(),
  1217. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1218. }
  1219. ////////////////////////////////////////////////////////////////////////////////////
  1220. // Change w:delText to w:t
  1221. if (element.Name == W.delText)
  1222. {
  1223. return new XElement(W.t,
  1224. element.Attributes(), // pulls in xml:space attribute
  1225. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1226. }
  1227. ////////////////////////////////////////////////////////////////////////////////////
  1228. // Identity transform
  1229. return new XElement(element.Name,
  1230. element.Attributes(),
  1231. element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
  1232. }
  1233. return node;
  1234. }
  1235. public static WmlDocument AcceptRevisions(WmlDocument document)
  1236. {
  1237. using (var streamDoc = new OpenXmlMemoryStreamDocument(document))
  1238. {
  1239. using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
  1240. {
  1241. AcceptRevisions(doc);
  1242. }
  1243. return streamDoc.GetModifiedWmlDocument();
  1244. }
  1245. }
  1246. public static void AcceptRevisions(WordprocessingDocument doc)
  1247. {
  1248. AcceptRevisionsForPart(doc.MainDocumentPart);
  1249. foreach (HeaderPart part in doc.MainDocumentPart.HeaderParts)
  1250. {
  1251. AcceptRevisionsForPart(part);
  1252. }
  1253. foreach (FooterPart part in doc.MainDocumentPart.FooterParts)
  1254. {
  1255. AcceptRevisionsForPart(part);
  1256. }
  1257. if (doc.MainDocumentPart.EndnotesPart is EndnotesPart endnotesPart)
  1258. {
  1259. AcceptRevisionsForPart(endnotesPart);
  1260. }
  1261. if (doc.MainDocumentPart.FootnotesPart is FootnotesPart footnotesPart)
  1262. {
  1263. AcceptRevisionsForPart(footnotesPart);
  1264. }
  1265. if (doc.MainDocumentPart.StyleDefinitionsPart is StyleDefinitionsPart styleDefinitionsPart)
  1266. {
  1267. AcceptRevisionsForStylesDefinitionPart(styleDefinitionsPart);
  1268. }
  1269. }
  1270. private static void AcceptRevisionsForStylesDefinitionPart(StyleDefinitionsPart stylesDefinitionsPart)
  1271. {
  1272. XElement root = stylesDefinitionsPart.GetXElement();
  1273. object newRoot = AcceptRevisionsForStylesTransform(root);
  1274. root.ReplaceWith(newRoot);
  1275. stylesDefinitionsPart.PutXElement();
  1276. }
  1277. private static object AcceptRevisionsForStylesTransform(XNode node)
  1278. {
  1279. if (node is XElement element)
  1280. {
  1281. return element.Name == W.pPrChange || element.Name == W.rPrChange
  1282. ? null
  1283. : new XElement(element.Name,
  1284. element.Attributes(),
  1285. element.Nodes().Select(AcceptRevisionsForStylesTransform));
  1286. }
  1287. return node;
  1288. }
  1289. public static void AcceptRevisionsForPart(OpenXmlPart part)
  1290. {
  1291. XElement documentElement = part.GetXDocument().Root;
  1292. documentElement = (XElement)RemoveRsidTransform(documentElement);
  1293. documentElement = (XElement)FixUpDeletedOrInsertedFieldCodesTransform(documentElement);
  1294. bool containsMoveFromMoveTo = documentElement.Descendants(W.moveFrom).Any();
  1295. documentElement = (XElement)AcceptMoveFromMoveToTransform(documentElement);
  1296. documentElement = AcceptMoveFromRanges(documentElement);
  1297. // AcceptParagraphEndTagsInMoveFromTransform needs rewritten similar to AcceptDeletedAndMoveFromParagraphMarks
  1298. documentElement = (XElement)AcceptParagraphEndTagsInMoveFromTransform(documentElement);
  1299. documentElement = AcceptDeletedAndMovedFromContentControls(documentElement);
  1300. documentElement = AcceptDeletedAndMoveFromParagraphMarks(documentElement);
  1301. if (containsMoveFromMoveTo)
  1302. {
  1303. documentElement = (XElement)RemoveRowsLeftEmptyByMoveFrom(documentElement);
  1304. }
  1305. documentElement = (XElement)AcceptAllOtherRevisionsTransform(documentElement);
  1306. documentElement = (XElement)AcceptDeletedCellsTransform(documentElement);
  1307. documentElement = (XElement)MergeAdjacentTablesTransform(documentElement);
  1308. documentElement = (XElement)AddEmptyParagraphToAnyEmptyCells(documentElement);
  1309. documentElement.Descendants().Attributes().Where(a => a.Name == PT.UniqueId || a.Name == PT.RunIds).Remove();
  1310. documentElement.Descendants(W.numPr).Where(np => !np.HasElements).Remove();
  1311. var newXDoc = new XDocument(documentElement);
  1312. part.PutXDocument(newXDoc);
  1313. }
  1314. // Note that AcceptRevisionsForElement is an incomplete implementation. It is not possible to accept all varieties of revisions
  1315. // for a single paragraph. The paragraph may contain a marker for a deleted or inserted content control, as one example, of
  1316. // which there are many. This method accepts simple revisions, such as deleted or inserted text, which is the most common use
  1317. // case.
  1318. public static XElement AcceptRevisionsForElement(XElement element)
  1319. {
  1320. XElement rElement = element;
  1321. rElement = (XElement)RemoveRsidTransform(rElement);
  1322. rElement = (XElement)AcceptMoveFromMoveToTransform(rElement);
  1323. rElement = (XElement)AcceptAllOtherRevisionsTransform(rElement);
  1324. rElement.Descendants().Attributes().Where(a => a.Name == PT.UniqueId || a.Name == PT.RunIds).Remove();
  1325. rElement.Descendants(W.numPr).Where(np => !np.HasElements).Remove();
  1326. return rElement;
  1327. }
  1328. private static object FixUpDeletedOrInsertedFieldCodesTransform(XNode node)
  1329. {
  1330. if (node is XElement element)
  1331. {
  1332. if (element.Name == W.p)
  1333. {
  1334. // 1 other
  1335. // 2 w:del/w:r/w:fldChar
  1336. // 3 w:ins/w:r/w:fldChar
  1337. // 4 w:instrText
  1338. // formulate new paragraph, looking for 4 that has 2 (or 3) before and after. Then put in a w:del (or w:ins), transforming w:instrText to w:delInstrText if w:del.
  1339. // transform 1, 2, 3 as usual
  1340. IEnumerable<int> groupedParaContentsKey = element.Elements().Select(e =>
  1341. {
  1342. if (e.Name == W.del && e.Elements(W.r).Elements(W.fldChar).Any())
  1343. {
  1344. return 2;
  1345. }
  1346. if (e.Name == W.ins && e.Elements(W.r).Elements(W.fldChar).Any())
  1347. {
  1348. return 3;
  1349. }
  1350. if (e.Name == W.r && e.Element(W.instrText) != null)
  1351. {
  1352. return 4;
  1353. }
  1354. return 1;
  1355. });
  1356. var zipped = element.Elements().Zip(groupedParaContentsKey, (e, k) => new { Ele = e, Key = k });
  1357. var grouped = zipped.GroupAdjacent(z => z.Key).ToArray();
  1358. int gLen = grouped.Length;
  1359. //if (gLen != 1)
  1360. // Console.WriteLine();
  1361. IEnumerable<object> newParaContents = grouped
  1362. .Select((g, i) =>
  1363. {
  1364. if (g.Key == 1 || g.Key == 2 || g.Key == 3)
  1365. {
  1366. return (object)g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
  1367. }
  1368. if (g.Key == 4)
  1369. {
  1370. if (i == 0 || i == gLen - 1)
  1371. {
  1372. return g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
  1373. }
  1374. if (grouped[i - 1].Key == 2 &&
  1375. grouped[i + 1].Key == 2)
  1376. {
  1377. return new XElement(W.del,
  1378. g.Select(gc => TransformInstrTextToDelInstrText(gc.Ele)));
  1379. }
  1380. if (grouped[i - 1].Key == 3 &&
  1381. grouped[i + 1].Key == 3)
  1382. {
  1383. return new XElement(W.ins,
  1384. g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele)));
  1385. }
  1386. return g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
  1387. }
  1388. throw new OpenXmlPowerToolsException("Internal error");
  1389. });
  1390. var newParagraph = new XElement(W.p,
  1391. element.Attributes(),
  1392. newParaContents);
  1393. return newParagraph;
  1394. }
  1395. return new XElement(element.Name,
  1396. element.Attributes(),
  1397. element.Nodes().Select(FixUpDeletedOrInsertedFieldCodesTransform));
  1398. }
  1399. return node;
  1400. }
  1401. private static object TransformInstrTextToDelInstrText(XNode node)
  1402. {
  1403. if (node is XElement element)
  1404. {
  1405. return element.Name == W.instrText
  1406. ? new XElement(W.delInstrText,
  1407. element.Attributes(),
  1408. element.Nodes())
  1409. : new XElement(element.Name,
  1410. element.Attributes(),
  1411. element.Nodes().Select(TransformInstrTextToDelInstrText));
  1412. }
  1413. return node;
  1414. }
  1415. private static object AddEmptyParagraphToAnyEmptyCells(XNode node)
  1416. {
  1417. if (node is XElement element)
  1418. {
  1419. return element.Name == W.tc && element.Elements().All(e => e.Name == W.tcPr)
  1420. ? new XElement(W.tc,
  1421. element.Attributes(),
  1422. element.Elements(),
  1423. new XElement(W.p))
  1424. : new XElement(element.Name,
  1425. element.Attributes(),
  1426. element.Nodes().Select(AddEmptyParagraphToAnyEmptyCells));
  1427. }
  1428. return node;
  1429. }
  1430. private static readonly Dictionary<XName, int> OrderTcPr = new Dictionary<XName, int>
  1431. {
  1432. { W.cnfStyle, 10 },
  1433. { W.tcW, 20 },
  1434. { W.gridSpan, 30 },
  1435. { W.hMerge, 40 },
  1436. { W.vMerge, 50 },
  1437. { W.tcBorders, 60 },
  1438. { W.shd, 70 },
  1439. { W.noWrap, 80 },
  1440. { W.tcMar, 90 },
  1441. { W.textDirection, 100 },
  1442. { W.tcFitText, 110 },
  1443. { W.vAlign, 120 },
  1444. { W.hideMark, 130 },
  1445. { W.headers, 140 }
  1446. };
  1447. private static XElement FixWidths(XElement tbl)
  1448. {
  1449. var newTbl = new XElement(tbl);
  1450. int[] gridLines = tbl.Elements(W.tblGrid).Elements(W.gridCol).Attributes(W._w).Select(w => (int)w).ToArray();
  1451. foreach (XElement tr in newTbl.Elements(W.tr))
  1452. {
  1453. var used = 0;
  1454. int lastUsed = -1;
  1455. foreach (XElement tc in tr.Elements(W.tc))
  1456. {
  1457. XAttribute tcW = tc.Elements(W.tcPr).Elements(W.tcW).Attributes(W._w).FirstOrDefault();
  1458. if (tcW != null)
  1459. {
  1460. int gridSpan = (int?)tc.Elements(W.tcPr).Elements(W.gridSpan).Attributes(W.val).FirstOrDefault() ?? 1;
  1461. int z = Math.Min(gridLines.Length - 1, lastUsed + gridSpan);
  1462. int w = gridLines.Where((g, i) => i > lastUsed && i <= z).Sum();
  1463. tcW.Value = w.ToString();
  1464. lastUsed += gridSpan;
  1465. used += gridSpan;
  1466. }
  1467. }
  1468. }
  1469. return newTbl;
  1470. }
  1471. private static object AcceptMoveFromMoveToTransform(XNode node)
  1472. {
  1473. if (node is XElement element)
  1474. {
  1475. if (element.Name == W.moveTo)
  1476. {
  1477. return element.Nodes().Select(AcceptMoveFromMoveToTransform);
  1478. }
  1479. if (element.Name == W.moveFrom)
  1480. {
  1481. return null;
  1482. }
  1483. return new XElement(element.Name,
  1484. element.Attributes(),
  1485. element.Nodes().Select(AcceptMoveFromMoveToTransform));
  1486. }
  1487. return node;
  1488. }
  1489. private static XElement AcceptMoveFromRanges(XElement document)
  1490. {
  1491. // The following lists contain the elements that are between start/end elements.
  1492. var startElementTagsInMoveFromRange = new List<XElement>();
  1493. var endElementTagsInMoveFromRange = new List<XElement>();
  1494. // Following are the elements that *may* be in a range that has both start and end elements.
  1495. var potentialDeletedElements = new Dictionary<string, PotentialInRangeElements>();
  1496. foreach (Tag tag in DescendantAndSelfTags(document))
  1497. {
  1498. if (tag.Element.Name == W.moveFromRangeStart)
  1499. {
  1500. string id = tag.Element.Attributes(W.id).First().Value;
  1501. potentialDeletedElements.Add(id, new PotentialInRangeElements());
  1502. continue;
  1503. }
  1504. if (tag.Element.Name == W.moveFromRangeEnd)
  1505. {
  1506. string id = tag.Element.Attributes(W.id).First().Value;
  1507. if (potentialDeletedElements.ContainsKey(id))
  1508. {
  1509. startElementTagsInMoveFromRange.AddRange(potentialDeletedElements[id].PotentialStartElementTagsInRange);
  1510. endElementTagsInMoveFromRange.AddRange(potentialDeletedElements[id].PotentialEndElementTagsInRange);
  1511. potentialDeletedElements.Remove(id);
  1512. }
  1513. continue;
  1514. }
  1515. if (potentialDeletedElements.Count > 0)
  1516. {
  1517. if (tag.TagType == TagTypeEnum.Element &&
  1518. tag.Element.Name != W.moveFromRangeStart &&
  1519. tag.Element.Name != W.moveFromRangeEnd)
  1520. {
  1521. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  1522. {
  1523. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  1524. }
  1525. continue;
  1526. }
  1527. if (tag.TagType == TagTypeEnum.EmptyElement &&
  1528. tag.Element.Name != W.moveFromRangeStart &&
  1529. tag.Element.Name != W.moveFromRangeEnd)
  1530. {
  1531. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  1532. {
  1533. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  1534. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  1535. }
  1536. continue;
  1537. }
  1538. if (tag.TagType == TagTypeEnum.EndElement &&
  1539. tag.Element.Name != W.moveFromRangeStart &&
  1540. tag.Element.Name != W.moveFromRangeEnd)
  1541. {
  1542. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  1543. {
  1544. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  1545. }
  1546. }
  1547. }
  1548. }
  1549. XElement[] moveFromElementsToDelete = startElementTagsInMoveFromRange
  1550. .Intersect(endElementTagsInMoveFromRange)
  1551. .ToArray();
  1552. if (moveFromElementsToDelete.Any())
  1553. {
  1554. return (XElement)AcceptMoveFromRangesTransform(
  1555. document, moveFromElementsToDelete);
  1556. }
  1557. return document;
  1558. }
  1559. private enum MoveFromCollectionType
  1560. {
  1561. ParagraphEndTagInMoveFromRange,
  1562. Other
  1563. }
  1564. private static object AcceptParagraphEndTagsInMoveFromTransform(XNode node)
  1565. {
  1566. if (node is XElement element)
  1567. {
  1568. if (W.BlockLevelContentContainers.Contains(element.Name))
  1569. {
  1570. List<IGrouping<MoveFromCollectionType, XElement>> groupedBodyChildren = element
  1571. .Elements()
  1572. .GroupAdjacent(c =>
  1573. {
  1574. BlockContentInfo pi = c.GetParagraphInfo();
  1575. if (pi.ThisBlockContentElement != null)
  1576. {
  1577. bool paragraphMarkIsInMoveFromRange =
  1578. pi.ThisBlockContentElement.Elements(W.moveFromRangeStart).Any() &&
  1579. !pi.ThisBlockContentElement.Elements(W.moveFromRangeEnd).Any();
  1580. if (paragraphMarkIsInMoveFromRange)
  1581. {
  1582. return MoveFromCollectionType.ParagraphEndTagInMoveFromRange;
  1583. }
  1584. }
  1585. XElement previousContentElement = c
  1586. .ContentElementsBeforeSelf()
  1587. .FirstOrDefault(e => e.GetParagraphInfo().ThisBlockContentElement != null);
  1588. if (previousContentElement != null)
  1589. {
  1590. BlockContentInfo pi2 = previousContentElement.GetParagraphInfo();
  1591. if (c.Name == W.p &&
  1592. pi2.ThisBlockContentElement.Elements(W.moveFromRangeStart).Any() &&
  1593. !pi2.ThisBlockContentElement.Elements(W.moveFromRangeEnd).Any())
  1594. {
  1595. return MoveFromCollectionType.ParagraphEndTagInMoveFromRange;
  1596. }
  1597. }
  1598. return MoveFromCollectionType.Other;
  1599. })
  1600. .ToList();
  1601. // If there is only one group, and it's key is MoveFromCollectionType.Other
  1602. // then there is nothing to do.
  1603. if (groupedBodyChildren.Count() == 1 &&
  1604. groupedBodyChildren.First().Key == MoveFromCollectionType.Other)
  1605. {
  1606. var newElement = new XElement(element.Name,
  1607. element.Attributes(),
  1608. groupedBodyChildren.Select(g =>
  1609. {
  1610. if (g.Key == MoveFromCollectionType.Other)
  1611. return (object)g;
  1612. // This is a transform that produces the first element in the
  1613. // collection, except that the paragraph in the descendents is
  1614. // replaced with a new paragraph that contains all contents of the
  1615. // existing paragraph, plus subsequent elements in the group
  1616. // collection, where the paragraph in each of those groups is
  1617. // collapsed.
  1618. return CoalesqueParagraphEndTagsInMoveFromTransform(g.First(), g);
  1619. }));
  1620. return newElement;
  1621. }
  1622. return new XElement(element.Name,
  1623. element.Attributes(),
  1624. element.Nodes().Select(AcceptParagraphEndTagsInMoveFromTransform));
  1625. }
  1626. return new XElement(element.Name,
  1627. element.Attributes(),
  1628. element.Nodes().Select(AcceptParagraphEndTagsInMoveFromTransform));
  1629. }
  1630. return node;
  1631. }
  1632. private static object AcceptAllOtherRevisionsTransform(XNode node)
  1633. {
  1634. if (node is XElement element)
  1635. {
  1636. // Accept inserted text, inserted paragraph marks, etc.
  1637. // Collapse all w:ins elements.
  1638. if (element.Name == W.ins)
  1639. {
  1640. return element.Nodes().Select(AcceptAllOtherRevisionsTransform);
  1641. }
  1642. // Remove all of the following elements. These elements are processed in:
  1643. // AcceptDeletedAndMovedFromContentControls
  1644. // AcceptMoveFromMoveToTransform
  1645. // AcceptDeletedAndMoveFromParagraphMarksTransform
  1646. // AcceptParagraphEndTagsInMoveFromTransform
  1647. // AcceptMoveFromRanges
  1648. if (element.Name == W.customXmlDelRangeStart ||
  1649. element.Name == W.customXmlDelRangeEnd ||
  1650. element.Name == W.customXmlInsRangeStart ||
  1651. element.Name == W.customXmlInsRangeEnd ||
  1652. element.Name == W.customXmlMoveFromRangeStart ||
  1653. element.Name == W.customXmlMoveFromRangeEnd ||
  1654. element.Name == W.customXmlMoveToRangeStart ||
  1655. element.Name == W.customXmlMoveToRangeEnd ||
  1656. element.Name == W.moveFromRangeStart ||
  1657. element.Name == W.moveFromRangeEnd ||
  1658. element.Name == W.moveToRangeStart ||
  1659. element.Name == W.moveToRangeEnd)
  1660. {
  1661. return null;
  1662. }
  1663. // Accept revisions in formatting on paragraphs.
  1664. // Accept revisions in formatting on runs.
  1665. // Accept revisions for applied styles to a table.
  1666. // Accept revisions for grid revisions to a table.
  1667. // Accept revisions for column properties.
  1668. // Accept revisions for row properties.
  1669. // Accept revisions for table level property exceptions.
  1670. // Accept revisions for section properties.
  1671. // Accept numbering revision in fields.
  1672. // Accept deleted field code text.
  1673. // Accept deleted literal text.
  1674. // Accept inserted cell.
  1675. if (element.Name == W.pPrChange ||
  1676. element.Name == W.rPrChange ||
  1677. element.Name == W.tblPrChange ||
  1678. element.Name == W.tblGridChange ||
  1679. element.Name == W.tcPrChange ||
  1680. element.Name == W.trPrChange ||
  1681. element.Name == W.tblPrExChange ||
  1682. element.Name == W.sectPrChange ||
  1683. element.Name == W.numberingChange ||
  1684. element.Name == W.delInstrText ||
  1685. element.Name == W.delText ||
  1686. element.Name == W.cellIns)
  1687. {
  1688. return null;
  1689. }
  1690. // Accept revisions for deleted math control character.
  1691. // Match m:f/m:fPr/m:ctrlPr/w:del, remove m:f.
  1692. if (element.Name == M.f &&
  1693. element.Elements(M.fPr).Elements(M.ctrlPr).Elements(W.del).Any())
  1694. {
  1695. return null;
  1696. }
  1697. // Accept revisions for deleted rows in tables.
  1698. // Match w:tr/w:trPr/w:del, remove w:tr.
  1699. if (element.Name == W.tr &&
  1700. element.Elements(W.trPr).Elements(W.del).Any())
  1701. {
  1702. return null;
  1703. }
  1704. // Accept deleted text in paragraphs.
  1705. if (element.Name == W.del)
  1706. {
  1707. return null;
  1708. }
  1709. // Accept revisions for vertically merged cells.
  1710. // cellMerge with a parent of tcPr, with attribute w:vMerge="rest" transformed
  1711. // to <w:vMerge w:val="restart"/>
  1712. // cellMerge with a parent of tcPr, with attribute w:vMerge="cont" transformed
  1713. // to <w:vMerge w:val="continue"/>
  1714. if (element.Name == W.cellMerge &&
  1715. element.Parent?.Name == W.tcPr &&
  1716. (string)element.Attribute(W.vMerge) == "rest")
  1717. {
  1718. return new XElement(W.vMerge,
  1719. new XAttribute(W.val, "restart"));
  1720. }
  1721. if (element.Name == W.cellMerge &&
  1722. element.Parent?.Name == W.tcPr &&
  1723. (string)element.Attribute(W.vMerge) == "cont")
  1724. {
  1725. return new XElement(W.vMerge,
  1726. new XAttribute(W.val, "continue"));
  1727. }
  1728. // Otherwise do identity clone.
  1729. return new XElement(element.Name,
  1730. element.Attributes(),
  1731. element.Nodes().Select(AcceptAllOtherRevisionsTransform));
  1732. }
  1733. return node;
  1734. }
  1735. private static object CollapseParagraphTransform(XNode node)
  1736. {
  1737. if (node is XElement element)
  1738. {
  1739. return element.Name == W.p
  1740. ? (object)element.Elements().Where(e => e.Name != W.pPr)
  1741. : new XElement(element.Name,
  1742. element.Attributes(),
  1743. element.Nodes().Select(CollapseParagraphTransform));
  1744. }
  1745. return node;
  1746. }
  1747. /// Accept deleted paragraphs.
  1748. ///
  1749. /// Group together all paragraphs that contain w:p/w:pPr/w:rPr/w:del elements. Make a
  1750. /// second group for the content element immediately following a paragraph that contains
  1751. /// a w:del element. The code uses the approach of dealing with paragraph content at
  1752. /// 'levels', ignoring paragraph content at other levels. Form a new paragraph that
  1753. /// contains the content of the grouped paragraphs with deleted paragraph marks, and the
  1754. /// content of the paragraph immediately following a paragraph that contains a deleted
  1755. /// paragraph mark. Include in the new paragraph the paragraph properties from the
  1756. /// paragraph following. When assembling the new paragraph, use a transform that collapses
  1757. /// the paragraph nodes when adding content, thereby preserving custom XML and content
  1758. /// controls.
  1759. private static void AnnotateBlockContentElements(XElement contentContainer)
  1760. {
  1761. // For convenience, there is a ParagraphInfo annotation on the contentContainer.
  1762. // It contains the same information as the ParagraphInfo annotation on the first
  1763. // paragraph.
  1764. if (contentContainer.Annotation<BlockContentInfo>() != null)
  1765. return;
  1766. XElement firstContentElement = contentContainer
  1767. .Elements()
  1768. .DescendantsAndSelf()
  1769. .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl);
  1770. if (firstContentElement == null)
  1771. return;
  1772. // Add the annotation on the contentContainer.
  1773. var currentContentInfo = new BlockContentInfo
  1774. {
  1775. PreviousBlockContentElement = null,
  1776. ThisBlockContentElement = firstContentElement,
  1777. NextBlockContentElement = null
  1778. };
  1779. // Add as annotation even though NextParagraph is not set yet.
  1780. contentContainer.AddAnnotation(currentContentInfo);
  1781. while (true)
  1782. {
  1783. currentContentInfo.ThisBlockContentElement.AddAnnotation(currentContentInfo);
  1784. // Find next sibling content element.
  1785. XElement nextContentElement;
  1786. XElement current = currentContentInfo.ThisBlockContentElement;
  1787. while (true)
  1788. {
  1789. nextContentElement = current
  1790. .ElementsAfterSelf()
  1791. .DescendantsAndSelf()
  1792. .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl);
  1793. if (nextContentElement != null)
  1794. {
  1795. currentContentInfo.NextBlockContentElement = nextContentElement;
  1796. break;
  1797. }
  1798. current = current.GetParent();
  1799. // When we've backed up the tree to the contentContainer, we're done.
  1800. if (current == contentContainer) return;
  1801. }
  1802. currentContentInfo = new BlockContentInfo
  1803. {
  1804. PreviousBlockContentElement = currentContentInfo.ThisBlockContentElement,
  1805. ThisBlockContentElement = nextContentElement,
  1806. NextBlockContentElement = null
  1807. };
  1808. }
  1809. }
  1810. private static IEnumerable<BlockContentInfo> IterateBlockContentElements(XElement element)
  1811. {
  1812. XElement current = element.Elements().FirstOrDefault();
  1813. if (current == null)
  1814. yield break;
  1815. AnnotateBlockContentElements(element);
  1816. var currentBlockContentInfo = element.Annotation<BlockContentInfo>();
  1817. if (currentBlockContentInfo != null)
  1818. {
  1819. while (true)
  1820. {
  1821. yield return currentBlockContentInfo;
  1822. if (currentBlockContentInfo?.NextBlockContentElement == null)
  1823. yield break;
  1824. currentBlockContentInfo = currentBlockContentInfo.NextBlockContentElement.Annotation<BlockContentInfo>();
  1825. }
  1826. }
  1827. }
  1828. [SuppressMessage("ReSharper", "InconsistentNaming")]
  1829. public static class PT
  1830. {
  1831. public static readonly XNamespace pt = "http://www.codeplex.com/PowerTools/2009/RevisionAccepter";
  1832. public static readonly XName UniqueId = pt + "UniqueId";
  1833. public static readonly XName RunIds = pt + "RunIds";
  1834. }
  1835. private static void AnnotateRunElementsWithId(XElement element)
  1836. {
  1837. var runId = 0;
  1838. foreach (XElement e in element.Descendants().Where(e => e.Name == W.r))
  1839. {
  1840. if (e.Name == W.r)
  1841. {
  1842. e.Add(new XAttribute(PT.UniqueId, runId++));
  1843. }
  1844. }
  1845. }
  1846. private static void AnnotateContentControlsWithRunIds(XElement element)
  1847. {
  1848. var sdtId = 0;
  1849. foreach (XElement e in element.Descendants(W.sdt))
  1850. {
  1851. // old version
  1852. //e.Add(new XAttribute(PT.RunIds,
  1853. // e.Descendants(W.r).Select(r => r.Attribute(PT.UniqueId).Value).StringConcatenate(s => s + ",").Trim(',')),
  1854. // new XAttribute(PT.UniqueId, sdtId++));
  1855. e.Add(new XAttribute(PT.RunIds,
  1856. e.DescendantsTrimmed(W.txbxContent)
  1857. .Where(d => d.Name == W.r)
  1858. .Select(r => r.Attribute(PT.UniqueId)?.Value)
  1859. .StringConcatenate(s => s + ",")
  1860. .Trim(',')),
  1861. new XAttribute(PT.UniqueId, sdtId++));
  1862. }
  1863. }
  1864. private static XElement AddBlockLevelContentControls(XElement newDocument, XElement original)
  1865. {
  1866. List<XElement> originalContentControls = original.Descendants(W.sdt).ToList();
  1867. List<XElement> existingContentControls = newDocument.Descendants(W.sdt).ToList();
  1868. IEnumerable<string> contentControlsToAdd = originalContentControls
  1869. .Select(occ => occ.Attribute(PT.UniqueId)?.Value)
  1870. .Except(existingContentControls.Select(ecc => ecc.Attribute(PT.UniqueId)?.Value));
  1871. foreach (XElement contentControl in originalContentControls
  1872. .Where(occ => contentControlsToAdd.Contains(occ.Attribute(PT.UniqueId)?.Value)))
  1873. {
  1874. // TODO - Need a slight modification here. If there is a paragraph
  1875. // in the content control that contains no runs, then the paragraph isn't included in the
  1876. // content control, because the following triggers off of runs.
  1877. // To see an example of this, see example document "NumberingParagraphPropertiesChange.docxs"
  1878. // find list of runs to surround
  1879. string[] runIds = contentControl.Attribute(PT.RunIds)?.Value.Split(',');
  1880. IEnumerable<XElement> runs = contentControl
  1881. .Descendants(W.r)
  1882. .Where(r => runIds != null && runIds.Contains(r.Attribute(PT.UniqueId)?.Value));
  1883. // find the runs in the new document
  1884. List<XElement> runsInNewDocument = runs
  1885. .Select(r => newDocument
  1886. .Descendants(W.r)
  1887. .First(z => z.Attribute(PT.UniqueId)?.Value == r.Attribute(PT.UniqueId)?.Value))
  1888. .ToList();
  1889. // find common ancestor
  1890. List<XElement> runAncestorIntersection = null;
  1891. foreach (XElement run in runsInNewDocument)
  1892. {
  1893. runAncestorIntersection = runAncestorIntersection == null
  1894. ? run.Ancestors().ToList()
  1895. : run.Ancestors().Intersect(runAncestorIntersection).ToList();
  1896. }
  1897. if (runAncestorIntersection == null) continue;
  1898. XElement commonAncestor = runAncestorIntersection.InDocumentOrder().Last();
  1899. // find child of common ancestor that contains first run
  1900. // find child of common ancestor that contains last run
  1901. // create new common ancestor:
  1902. // elements before first run child
  1903. // add content control, and runs from first run child to last run child
  1904. // elements after last run child
  1905. XElement firstRunChild = commonAncestor
  1906. .Elements()
  1907. .First(c => c.DescendantsAndSelf()
  1908. .Any(z => z.Name == W.r &&
  1909. z.Attribute(PT.UniqueId)?.Value == runsInNewDocument.First().Attribute(PT.UniqueId)?.Value));
  1910. XElement lastRunChild = commonAncestor
  1911. .Elements()
  1912. .First(c => c
  1913. .DescendantsAndSelf()
  1914. .Any(z => z.Name == W.r &&
  1915. z.Attribute(PT.UniqueId)?.Value == runsInNewDocument.Last().Attribute(PT.UniqueId)?.Value));
  1916. // If the list of runs for the content control is exactly the list of runs for the paragraph, then
  1917. // create the content control surrounding the paragraph, not surrounding the runs.
  1918. if (commonAncestor.Name == W.p &&
  1919. commonAncestor
  1920. .Elements()
  1921. .FirstOrDefault(e => e.Name != W.pPr &&
  1922. e.Name != W.commentRangeStart &&
  1923. e.Name != W.commentRangeEnd) == firstRunChild &&
  1924. commonAncestor
  1925. .Elements()
  1926. .LastOrDefault(e => e.Name != W.pPr &&
  1927. e.Name != W.commentRangeStart &&
  1928. e.Name != W.commentRangeEnd) == lastRunChild)
  1929. {
  1930. // TODO: Revisit. Why is newContentControl not used?
  1931. // replace commonAncestor with content control containing commonAncestor
  1932. var newContentControl = new XElement(contentControl.Name,
  1933. contentControl.Attributes(),
  1934. contentControl.Elements().Where(e => e.Name != W.sdtContent),
  1935. new XElement(W.sdtContent, commonAncestor));
  1936. var newContentControlOrdered = new XElement(contentControl.Name,
  1937. contentControl.Attributes(),
  1938. contentControl.Elements().OrderBy(e => OrderSdt.ContainsKey(e.Name) ? OrderSdt[e.Name] : 999));
  1939. commonAncestor.ReplaceWith(newContentControlOrdered);
  1940. continue;
  1941. }
  1942. List<XElement> elementsBeforeRange = commonAncestor
  1943. .Elements()
  1944. .TakeWhile(e => e != firstRunChild)
  1945. .ToList();
  1946. List<XElement> elementsInRange = commonAncestor
  1947. .Elements()
  1948. .SkipWhile(e => e != firstRunChild)
  1949. .TakeWhile(e => e != lastRunChild.ElementsAfterSelf().FirstOrDefault())
  1950. .ToList();
  1951. List<XElement> elementsAfterRange = commonAncestor
  1952. .Elements()
  1953. .SkipWhile(e => e != lastRunChild.ElementsAfterSelf().FirstOrDefault())
  1954. .ToList();
  1955. // detatch from current parent
  1956. commonAncestor.Elements().Remove();
  1957. var newContentControl2 = new XElement(contentControl.Name,
  1958. contentControl.Attributes(),
  1959. contentControl.Elements().Where(e => e.Name != W.sdtContent),
  1960. new XElement(W.sdtContent, elementsInRange));
  1961. var newContentControlOrdered2 = new XElement(newContentControl2.Name,
  1962. newContentControl2.Attributes(),
  1963. newContentControl2.Elements().OrderBy(e => OrderSdt.ContainsKey(e.Name) ? OrderSdt[e.Name] : 999));
  1964. commonAncestor.Add(
  1965. elementsBeforeRange,
  1966. newContentControlOrdered2,
  1967. elementsAfterRange);
  1968. }
  1969. return newDocument;
  1970. }
  1971. private static readonly Dictionary<XName, int> OrderSdt = new Dictionary<XName, int>
  1972. {
  1973. { W.sdtPr, 10 },
  1974. { W.sdtEndPr, 20 },
  1975. { W.sdtContent, 30 },
  1976. { W.bookmarkStart, 40 },
  1977. { W.bookmarkEnd, 50 }
  1978. };
  1979. private static XElement AcceptDeletedAndMoveFromParagraphMarks(XElement element)
  1980. {
  1981. AnnotateRunElementsWithId(element);
  1982. AnnotateContentControlsWithRunIds(element);
  1983. var newElement = (XElement)AcceptDeletedAndMoveFromParagraphMarksTransform(element);
  1984. XElement withBlockLevelContentControls = AddBlockLevelContentControls(newElement, element);
  1985. return withBlockLevelContentControls;
  1986. }
  1987. private enum GroupingType
  1988. {
  1989. DeletedRange,
  1990. Other
  1991. }
  1992. private class GroupingInfo
  1993. {
  1994. public GroupingType GroupingType;
  1995. public int GroupingKey;
  1996. }
  1997. private static object AcceptDeletedAndMoveFromParagraphMarksTransform(XNode node)
  1998. {
  1999. if (node is XElement element)
  2000. {
  2001. if (W.BlockLevelContentContainers.Contains(element.Name))
  2002. {
  2003. XElement bodySectPr = null;
  2004. if (element.Name == W.body)
  2005. bodySectPr = element.Element(W.sectPr);
  2006. var currentKey = 0;
  2007. var deletedParagraphGroupingInfo = new List<GroupingInfo>();
  2008. var state = 0; // 0 = in non deleted paragraphs
  2009. // 1 = in deleted paragraph
  2010. // 2 - paragraph following deleted paragraphs
  2011. foreach (BlockContentInfo c in IterateBlockContentElements(element))
  2012. {
  2013. if (c.ThisBlockContentElement.Name == W.p)
  2014. {
  2015. bool paragraphMarkIsDeletedOrMovedFrom = c
  2016. .ThisBlockContentElement
  2017. .Elements(W.pPr)
  2018. .Elements(W.rPr)
  2019. .Elements()
  2020. .Any(e => e.Name == W.del || e.Name == W.moveFrom);
  2021. if (paragraphMarkIsDeletedOrMovedFrom)
  2022. {
  2023. if (state == 0)
  2024. {
  2025. state = 1;
  2026. currentKey += 1;
  2027. deletedParagraphGroupingInfo.Add(
  2028. new GroupingInfo
  2029. {
  2030. GroupingType = GroupingType.DeletedRange,
  2031. GroupingKey = currentKey
  2032. });
  2033. continue;
  2034. }
  2035. if (state == 1)
  2036. {
  2037. deletedParagraphGroupingInfo.Add(
  2038. new GroupingInfo
  2039. {
  2040. GroupingType = GroupingType.DeletedRange,
  2041. GroupingKey = currentKey
  2042. });
  2043. continue;
  2044. }
  2045. if (state == 2)
  2046. {
  2047. state = 1;
  2048. currentKey += 1;
  2049. deletedParagraphGroupingInfo.Add(
  2050. new GroupingInfo
  2051. {
  2052. GroupingType = GroupingType.DeletedRange,
  2053. GroupingKey = currentKey
  2054. });
  2055. continue;
  2056. }
  2057. }
  2058. if (state == 0)
  2059. {
  2060. currentKey += 1;
  2061. deletedParagraphGroupingInfo.Add(
  2062. new GroupingInfo
  2063. {
  2064. GroupingType = GroupingType.Other,
  2065. GroupingKey = currentKey
  2066. });
  2067. }
  2068. else if (state == 1)
  2069. {
  2070. state = 2;
  2071. deletedParagraphGroupingInfo.Add(
  2072. new GroupingInfo
  2073. {
  2074. GroupingType = GroupingType.DeletedRange,
  2075. GroupingKey = currentKey
  2076. });
  2077. }
  2078. else if (state == 2)
  2079. {
  2080. state = 0;
  2081. currentKey += 1;
  2082. deletedParagraphGroupingInfo.Add(
  2083. new GroupingInfo
  2084. {
  2085. GroupingType = GroupingType.Other,
  2086. GroupingKey = currentKey
  2087. });
  2088. }
  2089. }
  2090. else if (c.ThisBlockContentElement.Name == W.tbl || c.ThisBlockContentElement.Name.Namespace == M.m)
  2091. {
  2092. currentKey += 1;
  2093. deletedParagraphGroupingInfo.Add(
  2094. new GroupingInfo
  2095. {
  2096. GroupingType = GroupingType.Other,
  2097. GroupingKey = currentKey
  2098. });
  2099. state = 0;
  2100. }
  2101. else
  2102. {
  2103. // otherwise keep the same state, put in the same group, and continue
  2104. deletedParagraphGroupingInfo.Add(
  2105. new GroupingInfo
  2106. {
  2107. GroupingType = GroupingType.Other,
  2108. GroupingKey = currentKey
  2109. });
  2110. }
  2111. }
  2112. var zipped = IterateBlockContentElements(element).Zip(deletedParagraphGroupingInfo, (blc, gi) => new
  2113. {
  2114. BlockLevelContent = blc,
  2115. GroupingInfo = gi
  2116. });
  2117. var groupedParagraphs = zipped
  2118. .GroupAdjacent(z => z.GroupingInfo.GroupingKey);
  2119. // Create a new block level content container.
  2120. var newBlockLevelContentContainer = new XElement(element.Name,
  2121. element.Attributes(),
  2122. element.Elements().Where(e => e.Name == W.tcPr),
  2123. groupedParagraphs.Select((g, i) =>
  2124. {
  2125. if (g.First().GroupingInfo.GroupingType == GroupingType.DeletedRange)
  2126. {
  2127. var newParagraph = new XElement(W.p,
  2128. #if false
  2129. // previously, this was set to g.First()
  2130. // however, this caused test [InlineData("RP/RP052-Deleted-Para-Mark.docx")] to lose paragraph numbering for a paragraph
  2131. // that we did not want to loose it for.
  2132. // the question is - when coalescing multiple paragraphs due to deleted paragraph marks, should we be taking the paragraph
  2133. // properties from the first or the last in the sequence of coalesced paragraph. It is possible that we should take Last
  2134. // when accepting revisions, but First when rejecting revisions.
  2135. g.First().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr),
  2136. #endif
  2137. g.Last().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr),
  2138. g.Select(z => CollapseParagraphTransform(z.BlockLevelContent.ThisBlockContentElement)));
  2139. // if this contains the last paragraph in the document, and if there is no content,
  2140. // and if the paragraph mark is deleted, then nuke the paragraph.
  2141. bool allIsDeleted = AllParaContentIsDeleted(newParagraph);
  2142. if (allIsDeleted &&
  2143. g.Last().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr).Elements(W.rPr)
  2144. .Elements(W.del).Any() &&
  2145. (g.Last().BlockLevelContent.NextBlockContentElement == null ||
  2146. g.Last().BlockLevelContent.NextBlockContentElement.Name == W.tbl))
  2147. return null;
  2148. return (object)newParagraph;
  2149. }
  2150. return g.Select(z =>
  2151. {
  2152. var newEle = new XElement(z.BlockLevelContent.ThisBlockContentElement.Name,
  2153. z.BlockLevelContent.ThisBlockContentElement.Attributes(),
  2154. z.BlockLevelContent.ThisBlockContentElement.Nodes()
  2155. .Select(AcceptDeletedAndMoveFromParagraphMarksTransform));
  2156. return newEle;
  2157. });
  2158. }),
  2159. bodySectPr);
  2160. return newBlockLevelContentContainer;
  2161. }
  2162. // Otherwise, identity clone.
  2163. return new XElement(element.Name,
  2164. element.Attributes(),
  2165. element.Nodes().Select(AcceptDeletedAndMoveFromParagraphMarksTransform));
  2166. }
  2167. return node;
  2168. }
  2169. // Determine if the paragraph contains any content that is not deleted.
  2170. private static bool AllParaContentIsDeleted(XElement p)
  2171. {
  2172. // needs collapse
  2173. // dir, bdo, sdt, ins, moveTo, smartTag
  2174. var testP = (XElement)CollapseTransform(p);
  2175. IEnumerable<XElement> childElements = testP.Elements();
  2176. IEnumerable<XElement> contentElements = childElements
  2177. .Where(ce =>
  2178. {
  2179. bool? b = IsRunContent(ce.Name);
  2180. if (b != null)
  2181. return (bool)b;
  2182. throw new Exception("Internal error 20, found element " + ce.Name.ToString());
  2183. });
  2184. return !contentElements.Any();
  2185. }
  2186. // dir, bdo, sdt, ins, moveTo, smartTag
  2187. private static object CollapseTransform(XNode node)
  2188. {
  2189. if (node is XElement element)
  2190. {
  2191. if (element.Name == W.dir ||
  2192. element.Name == W.bdr ||
  2193. element.Name == W.ins ||
  2194. element.Name == W.moveTo ||
  2195. element.Name == W.smartTag)
  2196. {
  2197. return element.Elements();
  2198. }
  2199. if (element.Name == W.sdt)
  2200. {
  2201. return element.Elements(W.sdtContent).Elements();
  2202. }
  2203. if (element.Name == W.pPr)
  2204. {
  2205. return null;
  2206. }
  2207. return new XElement(element.Name,
  2208. element.Attributes(),
  2209. element.Nodes().Select(CollapseTransform));
  2210. }
  2211. return node;
  2212. }
  2213. private static bool? IsRunContent(XName ceName)
  2214. {
  2215. // is content
  2216. // r, fldSimple, hyperlink, oMath, oMathPara, subDoc
  2217. if (ceName == W.r ||
  2218. ceName == W.fldSimple ||
  2219. ceName == W.hyperlink ||
  2220. ceName == W.subDoc ||
  2221. ceName == W.smartTag ||
  2222. ceName == W.smartTagPr ||
  2223. ceName.Namespace == M.m)
  2224. {
  2225. return true;
  2226. }
  2227. // not content
  2228. // bookmarkStart, bookmarkEnd, commentRangeStart, commentRangeEnd, del, moveFrom, proofErr
  2229. if (ceName == W.bookmarkStart ||
  2230. ceName == W.bookmarkEnd ||
  2231. ceName == W.commentRangeStart ||
  2232. ceName == W.commentRangeEnd ||
  2233. ceName == W.customXmlDelRangeStart ||
  2234. ceName == W.customXmlDelRangeEnd ||
  2235. ceName == W.customXmlInsRangeStart ||
  2236. ceName == W.customXmlInsRangeEnd ||
  2237. ceName == W.customXmlMoveFromRangeStart ||
  2238. ceName == W.customXmlMoveFromRangeEnd ||
  2239. ceName == W.customXmlMoveToRangeStart ||
  2240. ceName == W.customXmlMoveToRangeEnd ||
  2241. ceName == W.del ||
  2242. ceName == W.moveFrom ||
  2243. ceName == W.moveFromRangeStart ||
  2244. ceName == W.moveFromRangeEnd ||
  2245. ceName == W.moveToRangeStart ||
  2246. ceName == W.moveToRangeEnd ||
  2247. ceName == W.permStart ||
  2248. ceName == W.permEnd ||
  2249. ceName == W.proofErr)
  2250. {
  2251. return false;
  2252. }
  2253. return null;
  2254. }
  2255. private static IEnumerable<Tag> DescendantAndSelfTags(XElement element)
  2256. {
  2257. yield return new Tag
  2258. {
  2259. Element = element,
  2260. TagType = TagTypeEnum.Element
  2261. };
  2262. var iteratorStack = new Stack<IEnumerator<XElement>>();
  2263. iteratorStack.Push(element.Elements().GetEnumerator());
  2264. while (iteratorStack.Count > 0)
  2265. {
  2266. if (iteratorStack.Peek().MoveNext())
  2267. {
  2268. XElement currentXElement = iteratorStack.Peek().Current ??
  2269. throw new OpenXmlPowerToolsException("Internal error.");
  2270. if (!currentXElement.Nodes().Any())
  2271. {
  2272. yield return new Tag
  2273. {
  2274. Element = currentXElement,
  2275. TagType = TagTypeEnum.EmptyElement
  2276. };
  2277. continue;
  2278. }
  2279. yield return new Tag
  2280. {
  2281. Element = currentXElement,
  2282. TagType = TagTypeEnum.Element
  2283. };
  2284. iteratorStack.Push(currentXElement.Elements().GetEnumerator());
  2285. continue;
  2286. }
  2287. iteratorStack.Pop();
  2288. if (iteratorStack.Count > 0)
  2289. {
  2290. yield return new Tag
  2291. {
  2292. Element = iteratorStack.Peek().Current,
  2293. TagType = TagTypeEnum.EndElement
  2294. };
  2295. }
  2296. }
  2297. yield return new Tag
  2298. {
  2299. Element = element,
  2300. TagType = TagTypeEnum.EndElement
  2301. };
  2302. }
  2303. private class PotentialInRangeElements
  2304. {
  2305. public readonly List<XElement> PotentialStartElementTagsInRange;
  2306. public readonly List<XElement> PotentialEndElementTagsInRange;
  2307. public PotentialInRangeElements()
  2308. {
  2309. PotentialStartElementTagsInRange = new List<XElement>();
  2310. PotentialEndElementTagsInRange = new List<XElement>();
  2311. }
  2312. }
  2313. private enum TagTypeEnum
  2314. {
  2315. Element,
  2316. EndElement,
  2317. EmptyElement
  2318. }
  2319. private class Tag
  2320. {
  2321. public XElement Element;
  2322. public TagTypeEnum TagType;
  2323. }
  2324. private static object AcceptDeletedAndMovedFromContentControlsTransform(
  2325. XNode node,
  2326. XElement[] contentControlElementsToCollapse,
  2327. XElement[] moveFromElementsToDelete)
  2328. {
  2329. if (node is XElement element)
  2330. {
  2331. if (element.Name == W.sdt && contentControlElementsToCollapse.Contains(element))
  2332. {
  2333. return element
  2334. .Elements(W.sdtContent)
  2335. .Nodes()
  2336. .Select(n => AcceptDeletedAndMovedFromContentControlsTransform(n, contentControlElementsToCollapse,
  2337. moveFromElementsToDelete));
  2338. }
  2339. if (moveFromElementsToDelete.Contains(element))
  2340. return null;
  2341. return new XElement(element.Name,
  2342. element.Attributes(),
  2343. element
  2344. .Nodes()
  2345. .Select(n => AcceptDeletedAndMovedFromContentControlsTransform(n, contentControlElementsToCollapse,
  2346. moveFromElementsToDelete)));
  2347. }
  2348. return node;
  2349. }
  2350. private static XElement AcceptDeletedAndMovedFromContentControls(XElement documentRootElement)
  2351. {
  2352. // The following lists contain the elements that are between start/end elements.
  2353. var startElementTagsInDeleteRange = new List<XElement>();
  2354. var endElementTagsInDeleteRange = new List<XElement>();
  2355. var startElementTagsInMoveFromRange = new List<XElement>();
  2356. var endElementTagsInMoveFromRange = new List<XElement>();
  2357. // Following are the elements that *may* be in a range that has both start and end elements.
  2358. var potentialDeletedElements = new Dictionary<string, PotentialInRangeElements>();
  2359. var potentialMoveFromElements = new Dictionary<string, PotentialInRangeElements>();
  2360. foreach (Tag tag in DescendantAndSelfTags(documentRootElement))
  2361. {
  2362. if (tag.Element.Name == W.customXmlDelRangeStart)
  2363. {
  2364. string id = tag.Element.Attributes(W.id).First().Value;
  2365. potentialDeletedElements.Add(id, new PotentialInRangeElements());
  2366. continue;
  2367. }
  2368. if (tag.Element.Name == W.customXmlDelRangeEnd)
  2369. {
  2370. string id = tag.Element.Attributes(W.id).First().Value;
  2371. if (potentialDeletedElements.ContainsKey(id))
  2372. {
  2373. startElementTagsInDeleteRange.AddRange(potentialDeletedElements[id].PotentialStartElementTagsInRange);
  2374. endElementTagsInDeleteRange.AddRange(potentialDeletedElements[id].PotentialEndElementTagsInRange);
  2375. potentialDeletedElements.Remove(id);
  2376. }
  2377. continue;
  2378. }
  2379. if (tag.Element.Name == W.customXmlMoveFromRangeStart)
  2380. {
  2381. string id = tag.Element.Attributes(W.id).First().Value;
  2382. potentialMoveFromElements.Add(id, new PotentialInRangeElements());
  2383. continue;
  2384. }
  2385. if (tag.Element.Name == W.customXmlMoveFromRangeEnd)
  2386. {
  2387. string id = tag.Element.Attributes(W.id).First().Value;
  2388. if (potentialMoveFromElements.ContainsKey(id))
  2389. {
  2390. startElementTagsInMoveFromRange.AddRange(potentialMoveFromElements[id].PotentialStartElementTagsInRange);
  2391. endElementTagsInMoveFromRange.AddRange(potentialMoveFromElements[id].PotentialEndElementTagsInRange);
  2392. potentialMoveFromElements.Remove(id);
  2393. }
  2394. continue;
  2395. }
  2396. if (tag.Element.Name == W.sdt)
  2397. {
  2398. if (tag.TagType == TagTypeEnum.Element)
  2399. {
  2400. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  2401. {
  2402. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2403. }
  2404. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2405. {
  2406. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2407. }
  2408. continue;
  2409. }
  2410. if (tag.TagType == TagTypeEnum.EmptyElement)
  2411. {
  2412. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  2413. {
  2414. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2415. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2416. }
  2417. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2418. {
  2419. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2420. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2421. }
  2422. continue;
  2423. }
  2424. if (tag.TagType == TagTypeEnum.EndElement)
  2425. {
  2426. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialDeletedElements)
  2427. {
  2428. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2429. }
  2430. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2431. {
  2432. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2433. }
  2434. continue;
  2435. }
  2436. throw new PowerToolsInvalidDataException("Should not have reached this point.");
  2437. }
  2438. if (potentialMoveFromElements.Any() &&
  2439. tag.Element.Name != W.moveFromRangeStart &&
  2440. tag.Element.Name != W.moveFromRangeEnd &&
  2441. tag.Element.Name != W.customXmlMoveFromRangeStart &&
  2442. tag.Element.Name != W.customXmlMoveFromRangeEnd)
  2443. {
  2444. if (tag.TagType == TagTypeEnum.Element)
  2445. {
  2446. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2447. {
  2448. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2449. }
  2450. continue;
  2451. }
  2452. if (tag.TagType == TagTypeEnum.EmptyElement)
  2453. {
  2454. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2455. {
  2456. id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
  2457. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2458. }
  2459. continue;
  2460. }
  2461. if (tag.TagType == TagTypeEnum.EndElement)
  2462. {
  2463. foreach (KeyValuePair<string, PotentialInRangeElements> id in potentialMoveFromElements)
  2464. {
  2465. id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
  2466. }
  2467. }
  2468. }
  2469. }
  2470. XElement[] contentControlElementsToCollapse = startElementTagsInDeleteRange
  2471. .Intersect(endElementTagsInDeleteRange)
  2472. .ToArray();
  2473. XElement[] elementsToDeleteBecauseMovedFrom = startElementTagsInMoveFromRange
  2474. .Intersect(endElementTagsInMoveFromRange)
  2475. .ToArray();
  2476. if (contentControlElementsToCollapse.Length > 0 ||
  2477. elementsToDeleteBecauseMovedFrom.Length > 0)
  2478. {
  2479. object newDoc = AcceptDeletedAndMovedFromContentControlsTransform(documentRootElement,
  2480. contentControlElementsToCollapse, elementsToDeleteBecauseMovedFrom);
  2481. return newDoc as XElement;
  2482. }
  2483. return documentRootElement;
  2484. }
  2485. private static object AcceptMoveFromRangesTransform(
  2486. XNode node,
  2487. XElement[] elementsToDelete)
  2488. {
  2489. if (node is XElement element)
  2490. {
  2491. return elementsToDelete.Contains(element)
  2492. ? null
  2493. : new XElement(element.Name,
  2494. element.Attributes(),
  2495. element.Nodes().Select(n => AcceptMoveFromRangesTransform(n, elementsToDelete)));
  2496. }
  2497. return node;
  2498. }
  2499. private static object CoalesqueParagraphEndTagsInMoveFromTransform(
  2500. XNode node,
  2501. IGrouping<MoveFromCollectionType, XElement> g)
  2502. {
  2503. if (node is XElement element)
  2504. {
  2505. if (element.Name == W.p)
  2506. {
  2507. return new XElement(W.p,
  2508. element.Attributes(),
  2509. element.Elements(),
  2510. g.Skip(1).Select(CollapseParagraphTransform));
  2511. }
  2512. return new XElement(element.Name,
  2513. element.Attributes(),
  2514. element.Nodes().Select(n =>
  2515. CoalesqueParagraphEndTagsInMoveFromTransform(n, g)));
  2516. }
  2517. return node;
  2518. }
  2519. private enum DeletedCellCollectionType
  2520. {
  2521. DeletedCell,
  2522. Other
  2523. }
  2524. // For each table row, group deleted cells plus the cell before any deleted cell.
  2525. // Produce a new cell that has gridSpan set appropriately for group, and clone everything
  2526. // else.
  2527. private static object AcceptDeletedCellsTransform(XNode node)
  2528. {
  2529. if (node is XElement element)
  2530. {
  2531. if (element.Name == W.tr)
  2532. {
  2533. var groupedCells = element
  2534. .Elements()
  2535. .GroupAdjacent(e =>
  2536. {
  2537. XElement cellAfter = e.ElementsAfterSelf(W.tc).FirstOrDefault();
  2538. bool cellAfterIsDeleted = cellAfter != null &&
  2539. cellAfter.Descendants(W.cellDel).Any();
  2540. if (e.Name == W.tc &&
  2541. (cellAfterIsDeleted || e.Descendants(W.cellDel).Any()))
  2542. {
  2543. var a = new
  2544. {
  2545. CollectionType = DeletedCellCollectionType.DeletedCell,
  2546. Disambiguator = new[] { e }
  2547. .Concat(e.SiblingsBeforeSelfReverseDocumentOrder())
  2548. .FirstOrDefault(z => z.Name == W.tc && !z.Descendants(W.cellDel).Any())
  2549. };
  2550. return a;
  2551. }
  2552. var a2 = new
  2553. {
  2554. CollectionType = DeletedCellCollectionType.Other,
  2555. Disambiguator = e
  2556. };
  2557. return a2;
  2558. });
  2559. var tr = new XElement(W.tr,
  2560. element.Attributes(),
  2561. groupedCells.Select(g =>
  2562. {
  2563. if (g.Key.CollectionType == DeletedCellCollectionType.DeletedCell &&
  2564. g.First().Descendants(W.cellDel).Any())
  2565. {
  2566. return null;
  2567. }
  2568. if (g.Key.CollectionType == DeletedCellCollectionType.Other)
  2569. {
  2570. return (object)g;
  2571. }
  2572. XElement gridSpanElement = g
  2573. .First()
  2574. .Elements(W.tcPr)
  2575. .Elements(W.gridSpan)
  2576. .FirstOrDefault();
  2577. int gridSpan = gridSpanElement != null ? (int)gridSpanElement.Attribute(W.val) : 1;
  2578. int newGridSpan = gridSpan + g.Count() - 1;
  2579. XElement currentTcPr = g.First().Elements(W.tcPr).FirstOrDefault();
  2580. var newTcPr = new XElement(W.tcPr,
  2581. currentTcPr?.Attributes(),
  2582. new XElement(W.gridSpan,
  2583. new XAttribute(W.val, newGridSpan)),
  2584. currentTcPr?.Elements().Where(e => e.Name != W.gridSpan));
  2585. var orderedTcPr = new XElement(W.tcPr,
  2586. newTcPr.Elements().OrderBy(e => OrderTcPr.ContainsKey(e.Name) ? OrderTcPr[e.Name] : 999));
  2587. var newTc = new XElement(W.tc,
  2588. orderedTcPr,
  2589. g.First().Elements().Where(e => e.Name != W.tcPr));
  2590. return (object)newTc;
  2591. }));
  2592. return tr;
  2593. }
  2594. // Identity clone
  2595. return new XElement(element.Name,
  2596. element.Attributes(),
  2597. element.Nodes().Select(AcceptDeletedCellsTransform));
  2598. }
  2599. return node;
  2600. }
  2601. #if false
  2602. <w:tr>
  2603. <w:tc>
  2604. <w:tcPr>
  2605. <w:tcW w:w="5016"
  2606. w:type="dxa" />
  2607. </w:tcPr>
  2608. </w:tc>
  2609. </w:tr>
  2610. #endif
  2611. private static readonly XName[] BlockLevelElements =
  2612. {
  2613. W.p,
  2614. W.tbl,
  2615. W.sdt,
  2616. W.del,
  2617. W.ins,
  2618. M.oMath,
  2619. M.oMathPara,
  2620. W.moveTo
  2621. };
  2622. private static object RemoveRowsLeftEmptyByMoveFrom(XNode node)
  2623. {
  2624. if (node is XElement element)
  2625. {
  2626. if (element.Name == W.tr)
  2627. {
  2628. bool nonEmptyCells = element
  2629. .Elements(W.tc)
  2630. .Any(tc => tc.Elements().Any(tcc => BlockLevelElements.Contains(tcc.Name)));
  2631. if (nonEmptyCells)
  2632. {
  2633. return new XElement(element.Name,
  2634. element.Attributes(),
  2635. element.Nodes().Select(RemoveRowsLeftEmptyByMoveFrom));
  2636. }
  2637. return null;
  2638. }
  2639. return new XElement(element.Name,
  2640. element.Attributes(),
  2641. element.Nodes().Select(RemoveRowsLeftEmptyByMoveFrom));
  2642. }
  2643. return node;
  2644. }
  2645. public static readonly XName[] TrackedRevisionsElements =
  2646. {
  2647. W.cellDel,
  2648. W.cellIns,
  2649. W.cellMerge,
  2650. W.customXmlDelRangeEnd,
  2651. W.customXmlDelRangeStart,
  2652. W.customXmlInsRangeEnd,
  2653. W.customXmlInsRangeStart,
  2654. W.del,
  2655. W.delInstrText,
  2656. W.delText,
  2657. W.ins,
  2658. W.moveFrom,
  2659. W.moveFromRangeEnd,
  2660. W.moveFromRangeStart,
  2661. W.moveTo,
  2662. W.moveToRangeEnd,
  2663. W.moveToRangeStart,
  2664. W.numberingChange,
  2665. W.pPrChange,
  2666. W.rPrChange,
  2667. W.sectPrChange,
  2668. W.tblGridChange,
  2669. W.tblPrChange,
  2670. W.tblPrExChange,
  2671. W.tcPrChange,
  2672. W.trPrChange
  2673. };
  2674. public static bool PartHasTrackedRevisions(OpenXmlPart part)
  2675. {
  2676. return part.GetXDocument()
  2677. .Descendants()
  2678. .Any(e => TrackedRevisionsElements.Contains(e.Name));
  2679. }
  2680. public static bool HasTrackedRevisions(WmlDocument document)
  2681. {
  2682. using (var streamDoc = new OpenXmlMemoryStreamDocument(document))
  2683. {
  2684. using (WordprocessingDocument wdoc = streamDoc.GetWordprocessingDocument())
  2685. {
  2686. return RevisionAccepter.HasTrackedRevisions(wdoc);
  2687. }
  2688. }
  2689. }
  2690. public static bool HasTrackedRevisions(WordprocessingDocument doc)
  2691. {
  2692. if (PartHasTrackedRevisions(doc.MainDocumentPart))
  2693. {
  2694. return true;
  2695. }
  2696. if (doc.MainDocumentPart.HeaderParts.Any(PartHasTrackedRevisions))
  2697. {
  2698. return true;
  2699. }
  2700. if (doc.MainDocumentPart.FooterParts.Any(PartHasTrackedRevisions))
  2701. {
  2702. return true;
  2703. }
  2704. if (doc.MainDocumentPart.EndnotesPart is EndnotesPart endnotesPart)
  2705. {
  2706. if (PartHasTrackedRevisions(endnotesPart)) return true;
  2707. }
  2708. if (doc.MainDocumentPart.FootnotesPart is FootnotesPart footnotesPart)
  2709. {
  2710. if (PartHasTrackedRevisions(footnotesPart)) return true;
  2711. }
  2712. return false;
  2713. }
  2714. }
  2715. public partial class WmlDocument
  2716. {
  2717. public WmlDocument AcceptRevisions(WmlDocument document)
  2718. {
  2719. return RevisionAccepter.AcceptRevisions(document);
  2720. }
  2721. public bool HasTrackedRevisions(WmlDocument document)
  2722. {
  2723. return RevisionAccepter.HasTrackedRevisions(document);
  2724. }
  2725. }
  2726. public class BlockContentInfo
  2727. {
  2728. public XElement PreviousBlockContentElement;
  2729. public XElement ThisBlockContentElement;
  2730. public XElement NextBlockContentElement;
  2731. }
  2732. public static class RevisionAccepterExtensions
  2733. {
  2734. private static void InitializeParagraphInfo(XElement contentContext)
  2735. {
  2736. if (!W.BlockLevelContentContainers.Contains(contentContext.Name))
  2737. throw new ArgumentException(
  2738. "GetParagraphInfo called for element that is not child of content container");
  2739. XElement prev = null;
  2740. foreach (XElement content in contentContext.Elements())
  2741. {
  2742. // This may return null, indicating that there is no descendant paragraph. For
  2743. // example, comment elements have no descendant elements.
  2744. XElement paragraph = content
  2745. .DescendantsAndSelf()
  2746. .FirstOrDefault(e => e.Name == W.p || e.Name == W.tc || e.Name == W.txbxContent);
  2747. if (paragraph != null && (paragraph.Name == W.tc || paragraph.Name == W.txbxContent))
  2748. {
  2749. paragraph = null;
  2750. }
  2751. var pi = new BlockContentInfo
  2752. {
  2753. PreviousBlockContentElement = prev,
  2754. ThisBlockContentElement = paragraph
  2755. };
  2756. content.AddAnnotation(pi);
  2757. prev = content;
  2758. }
  2759. }
  2760. public static BlockContentInfo GetParagraphInfo(this XElement contentElement)
  2761. {
  2762. var paragraphInfo = contentElement.Annotation<BlockContentInfo>();
  2763. if (paragraphInfo != null)
  2764. return paragraphInfo;
  2765. InitializeParagraphInfo(contentElement.Parent);
  2766. return contentElement.Annotation<BlockContentInfo>();
  2767. }
  2768. public static IEnumerable<XElement> ContentElementsBeforeSelf(this XElement element)
  2769. {
  2770. XElement current = element;
  2771. while (true)
  2772. {
  2773. BlockContentInfo pi = current.GetParagraphInfo();
  2774. if (pi.PreviousBlockContentElement == null)
  2775. yield break;
  2776. yield return pi.PreviousBlockContentElement;
  2777. current = pi.PreviousBlockContentElement;
  2778. }
  2779. }
  2780. }
  2781. }
  2782. // Markup that this code processes:
  2783. //
  2784. // delText
  2785. // Method: AcceptAllOtherRevisionsTransform
  2786. // Sample document: MovedText.docx
  2787. // Reviewed: zeyad ***************************
  2788. // Semantics:
  2789. // Remove these elements.
  2790. // Reject:
  2791. // Transform to w:t element
  2792. //
  2793. // del (deleted run content)
  2794. // Method: AcceptAllOtherRevisionsTransform
  2795. // Reviewed: zeyad ***************************
  2796. // Semantics:
  2797. // Remove these elements and descendant elements.
  2798. // Reject:
  2799. // Transform to w:ins element
  2800. // Then Accept
  2801. //
  2802. // ins (inserted run content)
  2803. // Method: AcceptAllOtherRevisionsTransform
  2804. // Sample document: InsertedParagraphsAndRuns.docx
  2805. // Reviewed: zeyad ***************************
  2806. // Semantics:
  2807. // Collapse these elements.
  2808. // Reject:
  2809. // Transform to w:del element, and child w:t transform to w:delText element
  2810. // Then Accept
  2811. //
  2812. // ins (inserted paragraph)
  2813. // Method: AcceptAllOtherRevisionsTransform
  2814. // Sample document: InsertedParagraphsAndRuns.docx
  2815. // Reviewed: zeyad ***************************
  2816. // Semantics:
  2817. // Remove these elements.
  2818. // Reject:
  2819. // Transform to w:del element
  2820. // Then Accept
  2821. //
  2822. // del (deleted paragraph mark)
  2823. // Method: AcceptDeletedAndMoveFromParagraphMarksTransform
  2824. // Sample document: VariousTableRevisions.docx (deleted paragraph mark in paragraph in
  2825. // content control)
  2826. // Reviewed: tristan and zeyad ****************************************
  2827. // Semantics:
  2828. // Find all adjacent paragraps that have this element.
  2829. // Group adjacent paragraphs plus the paragraph following paragraph that has this element.
  2830. // Replace grouped paragraphs with a new paragraph containing the content from all grouped
  2831. // paragraphs. Use the paragraph properties from the first paragraph in the group.
  2832. // Reject:
  2833. // Transform to w:ins element
  2834. // Then Accept
  2835. //
  2836. // del (deleted table row)
  2837. // Method: AcceptAllOtherRevisionsTransform
  2838. // Sample document: VariousTableRevisions.docx
  2839. // Reviewed: zeyad ***************************
  2840. // Semantics:
  2841. // Match w:tr/w:trPr/w:del, remove w:tr.
  2842. // Reject:
  2843. // Transform to w:ins
  2844. // Then Accept
  2845. //
  2846. // ins (inserted table row)
  2847. // Method: AcceptAllOtherRevisionsTransform
  2848. // Sample document: VariousTableRevisions.docx
  2849. // Reviewed: zeyad ***************************
  2850. // Semantics:
  2851. // Remove these elements.
  2852. // Reject:
  2853. // Transform to w:del
  2854. // Then Accept
  2855. //
  2856. // del (deleted math control character)
  2857. // Method: AcceptAllOtherRevisionsTransform
  2858. // Sample document: DeletedMathControlCharacter.docx
  2859. // Reviewed: zeyad ***************************
  2860. // Semantics:
  2861. // Match m:f/m:fPr/m:ctrlPr/w:del, remove m:f.
  2862. // Reject:
  2863. // Transform to w:ins
  2864. // Then Accept
  2865. //
  2866. // ins (inserted math control character)
  2867. // Method: AcceptAllOtherRevisionsTransform
  2868. // Sample document: InsertedMathControlCharacter.docx
  2869. // Reviewed: zeyad ***************************
  2870. // Semantics:
  2871. // Remove these elements.
  2872. // Reject:
  2873. // Transform to w:del
  2874. // Then Accept
  2875. //
  2876. // moveTo (move destination paragraph mark)
  2877. // Method: AcceptMoveFromMoveToTransform
  2878. // Sample document: MovedText.docx
  2879. // Reviewed: zeyad ***************************
  2880. // Semantics:
  2881. // Remove these elements.
  2882. // Reject:
  2883. // Transform to moveFrom
  2884. // Then Accept
  2885. //
  2886. // moveTo (move destination run content)
  2887. // Method: AcceptMoveFromMoveToTransform
  2888. // Sample document: MovedText.docx
  2889. // Reviewed: zeyad ***************************
  2890. // Semantics:
  2891. // Collapse these elements.
  2892. // Reject:
  2893. // Transform to moveFrom
  2894. // Then Accept
  2895. //
  2896. // moveFrom (move source paragraph mark)
  2897. // Methods: AcceptDeletedAndMoveFromParagraphMarksTransform, AcceptParagraphEndTagsInMoveFromTransform
  2898. // Sample document: MovedText.docx
  2899. // Reviewed: tristan and zeyad ****************************************
  2900. // Semantics:
  2901. // Find all adjacent paragraps that have this element or deleted paragraph mark.
  2902. // Group adjacent paragraphs plus the paragraph following paragraph that has this element.
  2903. // Replace grouped paragraphs with a new paragraph containing the content from all grouped
  2904. // paragraphs.
  2905. // This is handled in the same code that handles del (deleted paragraph mark).
  2906. // Reject:
  2907. // Transform to moveTo
  2908. // Then Accept
  2909. //
  2910. // moveFrom (move source run content)
  2911. // Method: AcceptMoveFromMoveToTransform
  2912. // Sample document: MovedText.docx
  2913. // Reviewed: zeyad ***************************
  2914. // Semantics:
  2915. // Remove these elements.
  2916. // Reject:
  2917. // Transform to moveTo
  2918. // Then Accept
  2919. //
  2920. // moveFromRangeStart
  2921. // moveFromRangeEnd
  2922. // Method: AcceptMoveFromRanges
  2923. // Sample document: MovedText.docx
  2924. // Semantics:
  2925. // Find pairs of elements. Remove all elements that have both start and end tags in a
  2926. // range.
  2927. // Reject:
  2928. // Transform to moveToRangeStart, moveToRangeEnd
  2929. // Then Accept
  2930. //
  2931. // moveToRangeStart
  2932. // moveToRangeEnd
  2933. // Method: AcceptAllOtherRevisionsTransform
  2934. // Sample document: MovedText.docx
  2935. // Reviewed: zeyad ***************************
  2936. // Semantics:
  2937. // Remove these elements.
  2938. // Reject:
  2939. // Transform to moveFromRangeStart, moveFromRangeEnd
  2940. // Then Accept
  2941. //
  2942. // customXmlDelRangeStart
  2943. // customXmlDelRangeEnd
  2944. // customXmlMoveFromRangeStart
  2945. // customXmlMoveFromRangeEnd
  2946. // Method: AcceptDeletedAndMovedFromContentControls
  2947. // Reviewed: tristan and zeyad ****************************************
  2948. // Semantics:
  2949. // Find pairs of start/end elements, matching id attributes. Collapse sdt
  2950. // elements that have both start and end tags in a range.
  2951. // Reject:
  2952. // Transform to customXmlInsRangeStart, customXmlInsRangeEnd, customXmlMoveToRangeStart, customXmlMoveToRangeEnd
  2953. // Then Accept
  2954. //
  2955. // customXmlInsRangeStart
  2956. // customXmlInsRangeEnd
  2957. // customXmlMoveToRangeStart
  2958. // customXmlMoveToRangeEnd
  2959. // Method: AcceptAllOtherRevisionsTransform
  2960. // Reviewed: tristan and zeyad ****************************************
  2961. // Semantics:
  2962. // Remove these elements.
  2963. // Reject:
  2964. // Transform to customXmlDelRangeStart, customXmlDelRangeEnd, customXmlMoveFromRangeStart, customXmlMoveFromRangeEnd
  2965. // Then Accept
  2966. //
  2967. // delInstrText (deleted field code)
  2968. // Method: AcceptAllOtherRevisionsTransform
  2969. // Sample document: NumberingParagraphPropertiesChange.docx
  2970. // Reviewed: zeyad ***************************
  2971. // Semantics:
  2972. // Remove these elements.
  2973. // Reject:
  2974. // Transform to instrText
  2975. // Then Accept
  2976. // Note that instrText must be transformed to delInstrText when in a w:ins, in the same fashion that w:t must be transformed to w:delText when in w:ins
  2977. //
  2978. // ins (inserted numbering properties)
  2979. // Method: AcceptAllOtherRevisionsTransform
  2980. // Sample document: InsertedNumberingProperties.docx
  2981. // Reviewed: zeyad ***************************
  2982. // Semantics:
  2983. // Remove these elements.
  2984. // Reject
  2985. // Remove the containing w:numPr
  2986. //
  2987. // pPrChange (revision information for paragraph properties)
  2988. // Method: AcceptAllOtherRevisionsTransform
  2989. // Sample document: ParagraphAndRunPropertyRevisions.docx
  2990. // Reviewed: zeyad ***************************
  2991. // Semantics:
  2992. // Remove these elements.
  2993. // Reject:
  2994. // Replace pPr with the pPr in pPrChange
  2995. //
  2996. // rPrChange (revision information for run properties)
  2997. // Method: AcceptAllOtherRevisionsTransform
  2998. // Sample document: ParagraphAndRunPropertyRevisions.docx
  2999. // Sample document: VariousTableRevisions.docx
  3000. // Reviewed: zeyad ***************************
  3001. // Semantics:
  3002. // Remove these elements.
  3003. // Reject:
  3004. // Replace rPr with the rPr in rPrChange
  3005. //
  3006. // rPrChange (revision information for run properties on the paragraph mark)
  3007. // Method: AcceptAllOtherRevisionsTransform
  3008. // Sample document: ParagraphAndRunPropertyRevisions.docx
  3009. // Reviewed: zeyad ***************************
  3010. // Semantics:
  3011. // Remove these elements.
  3012. // Reject:
  3013. // Replace rPr with the rPr in rPrChange.
  3014. //
  3015. // numberingChange (previous numbering field properties)
  3016. // Method: AcceptAllOtherRevisionsTransform
  3017. // Sample document: NumberingFieldPropertiesChange.docx
  3018. // Semantics:
  3019. // Remove these elements.
  3020. // Reject:
  3021. // Remove these elements.
  3022. // These are there for numbering created via fields, and are not important.
  3023. //
  3024. // numberingChange (previous paragraph numbering properties)
  3025. // Method: AcceptAllOtherRevisionsTransform
  3026. // Sample document: NumberingFieldPropertiesChange.docx
  3027. // Semantics:
  3028. // Remove these elements.
  3029. // Reject:
  3030. // Remove these elements.
  3031. //
  3032. // sectPrChange
  3033. // Method: AcceptAllOtherRevisionsTransform
  3034. // Sample document: SectionPropertiesChange.docx
  3035. // Reviewed: zeyad ***************************
  3036. // Semantics:
  3037. // Remove these elements.
  3038. // Reject:
  3039. // Replace sectPr with the sectPr in sectPrChange
  3040. //
  3041. // tblGridChange
  3042. // Method: AcceptAllOtherRevisionsTransform
  3043. // Sample document: TableGridChange.docx
  3044. // Sample document: VariousTableRevisions.docx
  3045. // Reviewed: zeyad ***************************
  3046. // Semantics:
  3047. // Remove these elements.
  3048. // Reject:
  3049. // Replace tblGrid with the tblGrid in tblGridChange
  3050. //
  3051. // tblPrChange
  3052. // Method: AcceptAllOtherRevisionsTransform
  3053. // Sample document: TableGridChange.docx
  3054. // Sample document: VariousTableRevisions.docx
  3055. // Reviewed: zeyad ***************************
  3056. // Semantics:
  3057. // Remove these elements.
  3058. // Reject:
  3059. // Replace tblPr with the tblPr in tblPrChange
  3060. //
  3061. // tblPrExChange
  3062. // Method: AcceptAllOtherRevisionsTransform
  3063. // Sample document: VariousTableRevisions.docx
  3064. // Reviewed: zeyad ***************************
  3065. // Semantics:
  3066. // Remove these elements.
  3067. // Reject:
  3068. // Replace tblPrEx with the tblPrEx in tblPrExChange
  3069. //
  3070. // tcPrChange
  3071. // Method: AcceptAllOtherRevisionsTransform
  3072. // Sample document: TableGridChange.docx
  3073. // Sample document: VariousTableRevisions.docx
  3074. // Reviewed: zeyad ***************************
  3075. // Semantics:
  3076. // Remove these elements.
  3077. // Reject:
  3078. // Replace tcPr with the tcPr in tcPrChange
  3079. //
  3080. // trPrChange
  3081. // Method: AcceptAllOtherRevisionsTransform
  3082. // Sample document: VariousTableRevisions.docx
  3083. // Reviewed: zeyad ***************************
  3084. // Semantics:
  3085. // Remove these elements.
  3086. // Reject:
  3087. // Replace trPr with the trPr in trPrChange
  3088. //
  3089. // celDel
  3090. // Method: AcceptDeletedCellsTransform
  3091. // Sample document: HorizontallyMergedCells.docx
  3092. // Semantics:
  3093. // Group consecutive deleted cells, and remove them.
  3094. // Adjust the cell before deleted cells:
  3095. // Increase gridSpan by the number of deleted cells that are removed.
  3096. // Reject:
  3097. // Remove this element
  3098. //
  3099. // celIns
  3100. // Method: AcceptAllOtherRevisionsTransform
  3101. // Sample document: HorizontallyMergedCells11.docx
  3102. // Semantics:
  3103. // Remove these elements.
  3104. // Reject:
  3105. // If a w:tc contains w:tcPr/w:cellIns, then remove the cell
  3106. //
  3107. // cellMerge
  3108. // Method: AcceptAllOtherRevisionsTransform
  3109. // Sample document: MergedCell.docx
  3110. // Semantics:
  3111. // Transform cellMerge with a parent of tcPr, with attribute w:vMerge="rest"
  3112. // to <w:vMerge w:val="restart"/>.
  3113. // Transform cellMerge with a parent of tcPr, with attribute w:vMerge="cont"
  3114. // to <w:vMerge w:val="continue"/>
  3115. //
  3116. // The following items need to be addressed in a future release:
  3117. // - inserted run inside deleted paragraph - moveTo is same as insert
  3118. // - must increase w:val attribute of the w:gridSpan element of the
  3119. // cell immediately preceding the group of deleted cells by the
  3120. // ***sum*** of the values of the w:val attributes of w:gridSpan
  3121. // elements of each of the deleted cells.