RevisionProcessor.cs 133 KB

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