123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237 |
- // Copyright (c) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Xml.Linq;
- using DocumentFormat.OpenXml.Packaging;
- namespace OpenXmlPowerTools
- {
- class ReverseRevisionsInfo
- {
- public bool InInsert;
- }
- public class RevisionProcessor
- {
- public static WmlDocument RejectRevisions(WmlDocument document)
- {
- using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
- {
- using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
- {
- RejectRevisions(doc);
- }
- return streamDoc.GetModifiedWmlDocument();
- }
- }
- public static void RejectRevisions(WordprocessingDocument doc)
- {
- RejectRevisionsForPart(doc.MainDocumentPart);
- foreach (var part in doc.MainDocumentPart.HeaderParts)
- RejectRevisionsForPart(part);
- foreach (var part in doc.MainDocumentPart.FooterParts)
- RejectRevisionsForPart(part);
- if (doc.MainDocumentPart.EndnotesPart != null)
- RejectRevisionsForPart(doc.MainDocumentPart.EndnotesPart);
- if (doc.MainDocumentPart.FootnotesPart != null)
- RejectRevisionsForPart(doc.MainDocumentPart.FootnotesPart);
- if (doc.MainDocumentPart.StyleDefinitionsPart != null)
- RejectRevisionsForStylesDefinitionPart(doc.MainDocumentPart.StyleDefinitionsPart);
- ReverseRevisions(doc);
- AcceptRevisionsForPart(doc.MainDocumentPart);
- foreach (var part in doc.MainDocumentPart.HeaderParts)
- AcceptRevisionsForPart(part);
- foreach (var part in doc.MainDocumentPart.FooterParts)
- AcceptRevisionsForPart(part);
- if (doc.MainDocumentPart.EndnotesPart != null)
- AcceptRevisionsForPart(doc.MainDocumentPart.EndnotesPart);
- if (doc.MainDocumentPart.FootnotesPart != null)
- AcceptRevisionsForPart(doc.MainDocumentPart.FootnotesPart);
- if (doc.MainDocumentPart.StyleDefinitionsPart != null)
- AcceptRevisionsForStylesDefinitionPart(doc.MainDocumentPart.StyleDefinitionsPart);
- }
- // Reject revisions for those revisions that can't be rejected by inverting the sense of the revision, and then accepting.
- private static void RejectRevisionsForPart(OpenXmlPart part)
- {
- var xDoc = part.GetXDocument();
- var newRoot = RejectRevisionsForPartTransform(xDoc.Root);
- xDoc.Root.ReplaceWith(newRoot);
- part.PutXDocument();
- }
- private static object RejectRevisionsForPartTransform(XNode node)
- {
- var element = node as XElement;
- if (element != null)
- {
- ////////////////////////////////////////////////////////////////////////////////////////
- // Inserted Numbering Properties
- #if false
- <w:p>
- <w:pPr>
- <w:pStyle w:val="ListParagraph"/>
- <w:numPr>
- <w:ilvl w:val="0"/>
- <w:numId w:val="1"/>
- <w:ins w:id="0" w:author="Eric White" w:date="2017-03-26T03:50:00Z" />
- </w:numPr>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- </w:pPr>
- <w:r w:rsidRPr="009D59B3">
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>This is a test.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.numPr && element.Element(W.ins) != null)
- return null;
- ////////////////////////////////////////////////////////////////////////////////////////
- // Paragraph properties change
- #if false
- <w:p>
- <w:pPr>
- <w:pStyle w:val="ListParagraph"/>
- <w:numPr>
- <w:ilvl w:val="1"/>
- <w:numId w:val="2"/>
- </w:numPr>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:pPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T04:55:00Z">
- <w:pPr>
- <w:pStyle w:val="ListParagraph"/>
- <w:numPr>
- <w:ilvl w:val="1"/>
- <w:numId w:val="1"/>
- </w:numPr>
- <w:ind w:left="1440" w:hanging="360"/>
- </w:pPr>
- </w:pPrChange>
- </w:pPr>
- <w:r>
- <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.pPr &&
- element.Element(W.pPrChange) != null)
- {
- var pPr = element.Element(W.pPrChange).Element(W.pPr);
- if (pPr == null)
- pPr = new XElement(W.pPr);
- var new_pPr = new XElement(pPr); // clone it
- new_pPr.Add(RejectRevisionsForPartTransform(element.Element(W.rPr)));
- return RejectRevisionsForPartTransform(new_pPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Run properties change
- #if false
- <w:p w:rsidR="00615148" w:rsidRPr="00615148" w:rsidRDefault="00615148">
- <w:pPr>
- <w:rPr>
- <w:b/>
- <w:lang w:val="en-US"/>
- <w:rPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T05:02:00Z">
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- </w:rPrChange>
- </w:rPr>
- </w:pPr>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
- </w:r>
- <w:bookmarkStart w:id="1" w:name="_GoBack"/>
- </w:p>
- #endif
- if (element.Name == W.rPr &&
- element.Element(W.rPrChange) != null)
- {
- var new_rPr = element.Element(W.rPrChange).Element(W.rPr);
- return RejectRevisionsForPartTransform(new_rPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Field code numbering change
- #if false
- <w:p w:rsidR="00D46247" w:rsidRDefault="00D46247">
- <w:r>
- <w:fldChar w:fldCharType="begin"/>
- </w:r>
- <w:r>
- <w:instrText xml:space="preserve"> LISTNUM </w:instrText>
- </w:r>
- <w:r>
- <w:fldChar w:fldCharType="end">
- <w:numberingChange w:id="0" w:author="Eric White" w:date="2017-03-26T12:48:00Z" w:original="1)"/>
- </w:fldChar>
- </w:r>
- <w:r>
- <w:t xml:space="preserve"> Video provides a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.numberingChange)
- return null;
- ////////////////////////////////////////////////////////////////////////////////////
- // Change w:sectPr
- #if false
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:ins w:id="0" w:author="Eric White" w:date="2017-03-26T15:40:00Z"/>
- </w:rPr>
- <w:sectPr>
- <w:pgSz w:w="12240" w:h="15840"/>
- <w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720" w:header="720" w:footer="720" w:gutter="0"/>
- <w:cols w:space="720"/>
- <w:docGrid w:linePitch="360"/>
- <w:sectPrChange w:id="1" w:author="Eric White" w:date="2017-03-26T15:42:00Z">
- <w:sectPr w:rsidR="00620990" w:rsidSect="004E0757">
- <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
- </w:sectPr>
- </w:sectPrChange>
- </w:sectPr>
- </w:pPr>
- </w:p>
- #endif
- if (element.Name == W.sectPr &&
- element.Element(W.sectPrChange) != null)
- {
- var newSectPr = element.Element(W.sectPrChange).Element(W.sectPr);
- return RejectRevisionsForPartTransform(newSectPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // tblGridChange
- #if false
- <w:tblGrid>
- <w:gridCol w:w="1525"/>
- <w:gridCol w:w="3005"/>
- <w:gridCol w:w="3006"/>
- <w:tblGridChange w:id="1">
- <w:tblGrid>
- <w:gridCol w:w="3005"/>
- <w:gridCol w:w="3005"/>
- <w:gridCol w:w="3006"/>
- </w:tblGrid>
- </w:tblGridChange>
- </w:tblGrid>
- #endif
- if (element.Name == W.tblGrid &&
- element.Element(W.tblGridChange) != null)
- {
- var newTblGrid = element.Element(W.tblGridChange).Element(W.tblGrid);
- return RejectRevisionsForPartTransform(newTblGrid);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // tcPrChange
- #if false
- <w:tc>
- <w:tcPr>
- <w:tcW w:w="1525" w:type="dxa"/>
- <w:tcPrChange w:id="2" w:author="Eric White" w:date="2017-03-26T18:01:00Z">
- <w:tcPr>
- <w:tcW w:w="3005" w:type="dxa"/>
- </w:tcPr>
- </w:tcPrChange>
- </w:tcPr>
- <w:p>
- <w:r>
- <w:t>1</w:t>
- </w:r>
- </w:p>
- </w:tc>
- #endif
- if (element.Name == W.tcPr &&
- element.Element(W.tcPrChange) != null)
- {
- var newTcPr = element.Element(W.tcPrChange).Element(W.tcPr);
- return RejectRevisionsForPartTransform(newTcPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // trPrChange
- if (element.Name == W.trPr &&
- element.Element(W.trPrChange) != null)
- {
- var newTrPr = element.Element(W.trPrChange).Element(W.trPr);
- return RejectRevisionsForPartTransform(newTrPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // tblPrExChange
- #if false
- <w:tblPrEx>
- <w:tblW w:w="0" w:type="auto"/>
- <w:tblPrExChange w:id="1" w:author="Eric White" w:date="2017-03-26T18:10:00Z">
- <w:tblPrEx>
- <w:tblW w:w="0" w:type="auto"/>
- </w:tblPrEx>
- </w:tblPrExChange>
- </w:tblPrEx>
- #endif
- #if false
- <w:tr w:rsidR="00097582" w:rsidTr="00F843C4">
- <w:tblPrEx>
- <w:tblW w:w="0" w:type="auto"/>
- <w:tblBorders>
- <w:top w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:left w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:bottom w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:right w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:insideH w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:insideV w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- </w:tblBorders>
- <w:tblPrExChange w:id="1" w:author="Eric White" w:date="2017-03-26T20:38:00Z">
- <w:tblPrEx>
- <w:tblW w:w="0" w:type="auto"/>
- <w:tblBorders>
- <w:top w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:left w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:bottom w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:right w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:insideH w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- <w:insideV w:val="thickThinMediumGap" w:sz="24" w:space="0" w:color="auto"/>
- </w:tblBorders>
- </w:tblPrEx>
- </w:tblPrExChange>
- </w:tblPrEx>
- #endif
- if (element.Name == W.tblPrEx &&
- element.Element(W.tblPrExChange) != null)
- {
- var newTblPrEx = element.Element(W.tblPrExChange).Element(W.tblPrEx);
- return RejectRevisionsForPartTransform(newTblPrEx);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // tblPrChange
- #if false
- <w:tbl>
- <w:tblPr>
- <w:tblStyle w:val="GridTable4-Accent1"/>
- <w:tblW w:w="0" w:type="auto"/>
- <w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
- <w:tblPrChange w:id="0" w:author="Eric White" w:date="2017-03-26T20:05:00Z">
- <w:tblPr>
- <w:tblStyle w:val="TableGrid"/>
- <w:tblW w:w="0" w:type="auto"/>
- <w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
- </w:tblPr>
- </w:tblPrChange>
- </w:tblPr>
- #endif
- if (element.Name == W.tblPr &&
- element.Element(W.tblPrChange) != null)
- {
- var newTrPr = element.Element(W.tblPrChange).Element(W.tblPr);
- return RejectRevisionsForPartTransform(newTrPr);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // tblPrChange
- #if false
- <w:tc>
- <w:tcPr>
- <w:tcW w:w="3005" w:type="dxa"/>
- <w:cellDel w:id="8" w:author="Eric White" w:date="2017-03-26T21:12:00Z"/>
- <w:tcPrChange w:id="9" w:author="Eric White" w:date="2017-03-26T21:12:00Z">
- <w:tcPr>
- <w:tcW w:w="3005" w:type="dxa"/>
- <w:gridSpan w:val="2"/>
- <w:cellDel w:id="10" w:author="Eric White" w:date="2017-03-26T21:12:00Z"/>
- </w:tcPr>
- </w:tcPrChange>
- </w:tcPr>
- #endif
- if (element.Name == W.cellDel ||
- element.Name == W.cellMerge)
- return null;
- if (element.Name == W.tc &&
- element.Elements(W.tcPr).Elements(W.cellIns).Any())
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => RejectRevisionsForPartTransform(n)));
- }
- return node;
- }
- private static void RejectRevisionsForStylesDefinitionPart(StyleDefinitionsPart stylesDefinitionsPart)
- {
- var xDoc = stylesDefinitionsPart.GetXDocument();
- var newRoot = RejectRevisionsForStylesTransform(xDoc.Root);
- xDoc.Root.ReplaceWith(newRoot);
- stylesDefinitionsPart.PutXDocument();
- }
- private static object RejectRevisionsForStylesTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.pPr &&
- element.Element(W.pPrChange) != null)
- {
- var new_pPr = element.Element(W.pPrChange).Element(W.pPr);
- return RejectRevisionsForStylesTransform(new_pPr);
- }
- if (element.Name == W.rPr &&
- element.Element(W.rPrChange) != null)
- {
- var new_rPr = element.Element(W.rPrChange).Element(W.rPr);
- return RejectRevisionsForStylesTransform(new_rPr);
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => RejectRevisionsForStylesTransform(n)));
- }
- return node;
- }
- private static void ReverseRevisions(WordprocessingDocument doc)
- {
- ReverseRevisionsForPart(doc.MainDocumentPart);
- foreach (var part in doc.MainDocumentPart.HeaderParts)
- ReverseRevisionsForPart(part);
- foreach (var part in doc.MainDocumentPart.FooterParts)
- ReverseRevisionsForPart(part);
- if (doc.MainDocumentPart.EndnotesPart != null)
- ReverseRevisionsForPart(doc.MainDocumentPart.EndnotesPart);
- if (doc.MainDocumentPart.FootnotesPart != null)
- ReverseRevisionsForPart(doc.MainDocumentPart.FootnotesPart);
- }
- private static void ReverseRevisionsForPart(OpenXmlPart part)
- {
- var xDoc = part.GetXDocument();
- ReverseRevisionsInfo rri = new ReverseRevisionsInfo();
- rri.InInsert = false;
- var newRoot = (XElement)ReverseRevisionsTransform(xDoc.Root, rri);
- newRoot = (XElement)RemoveRsidTransform(newRoot);
- xDoc.Root.ReplaceWith(newRoot);
- part.PutXDocument();
- }
- private static object RemoveRsidTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.rsid)
- return null;
- return new XElement(element.Name,
- element.Attributes().Where(a => a.Name != W.rsid &&
- a.Name != W.rsidDel &&
- a.Name != W.rsidP &&
- a.Name != W.rsidR &&
- a.Name != W.rsidRDefault &&
- a.Name != W.rsidRPr &&
- a.Name != W.rsidSect &&
- a.Name != W.rsidTr),
- element.Nodes().Select(n => RemoveRsidTransform(n)));
- }
- return node;
- }
- private static object MergeAdjacentTablesTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Element(W.tbl) != null)
- {
- var grouped = element
- .Elements()
- .GroupAdjacent(e =>
- {
- if (e.Name != W.tbl)
- return "";
- var bidiVisual = e.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
- var bidiVisString = bidiVisual == null ? "" : "|bidiVisual";
- var key = "tbl" + bidiVisString;
- return key;
- });
- var newContent = grouped
- .Select(g =>
- {
- if (g.Key == "" || g.Count() == 1)
- return (object)g;
- var rolled = g
- .Select(tbl =>
- {
- var gridCols = tbl
- .Elements(W.tblGrid)
- .Elements(W.gridCol)
- .Attributes(W._w)
- .Select(a => (int)a)
- .Rollup(0, (s, i) => s + i);
- return gridCols;
- })
- .SelectMany(m => m)
- .Distinct()
- .OrderBy(w => w)
- .ToArray();
- var newTable = new XElement(W.tbl,
- g.First().Elements(W.tblPr),
- new XElement(W.tblGrid,
- rolled.Select((r, i) =>
- {
- int v;
- if (i == 0)
- v = r;
- else
- v = r - rolled[i - 1];
- return new XElement(W.gridCol,
- new XAttribute(W._w, v));
- })),
- g.Select(tbl =>
- {
- var fixedWidthsTbl = FixWidths(tbl);
- var newRows = fixedWidthsTbl.Elements(W.tr)
- .Select(tr =>
- {
- XElement newRow = new XElement(W.tr,
- tr.Attributes(),
- tr.Elements().Where(e => e.Name != W.tc),
- tr.Elements(W.tc).Select(tc =>
- {
- int? w = (int?)tc
- .Elements(W.tcPr)
- .Elements(W.tcW)
- .Attributes(W._w)
- .FirstOrDefault();
- if (w == null)
- return tc;
- var cellsToLeft = tc
- .Parent
- .Elements(W.tc)
- .TakeWhile(btc => btc != tc);
- int widthToLeft = 0;
- if (cellsToLeft.Any())
- widthToLeft = cellsToLeft
- .Elements(W.tcPr)
- .Elements(W.tcW)
- .Attributes(W._w)
- .Select(wi => (int)wi)
- .Sum();
- var rolledPairs = new[] { new
- {
- GridValue = 0,
- Index = 0,
- }}
- .Concat(
- rolled
- .Select((r, i) => new
- {
- GridValue = r,
- Index = i + 1,
- }));
- var start = rolledPairs
- .FirstOrDefault(t => t.GridValue >= widthToLeft);
- if (start != null)
- {
- var gridsRequired = rolledPairs
- .Skip(start.Index)
- .TakeWhile(rp => rp.GridValue - start.GridValue < w)
- .Count();
- var tcPr = new XElement(W.tcPr,
- tc.Elements(W.tcPr).Elements().Where(e => e.Name != W.gridSpan),
- gridsRequired != 1 ?
- new XElement(W.gridSpan,
- new XAttribute(W.val, gridsRequired)) :
- null);
- var orderedTcPr = new XElement(W.tcPr,
- tcPr.Elements().OrderBy(e =>
- {
- if (Order_tcPr.ContainsKey(e.Name))
- return Order_tcPr[e.Name];
- return 999;
- }));
- var newCell = new XElement(W.tc,
- orderedTcPr,
- tc.Elements().Where(e => e.Name != W.tcPr));
- return newCell;
- }
- return tc;
- }));
- return newRow;
- });
- return newRows;
- }));
- return newTable;
- });
- return new XElement(element.Name,
- element.Attributes(),
- newContent);
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => MergeAdjacentTablesTransform(n)));
- }
- return node;
- }
- private static object ReverseRevisionsTransform(XNode node, ReverseRevisionsInfo rri)
- {
- var element = node as XElement;
- if (element != null)
- {
- var parent = element
- .Ancestors()
- .Where(a => a.Name != W.sdtContent && a.Name != W.sdt && a.Name != W.smartTag)
- .FirstOrDefault();
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted run
- #if false
- <w:p>
- <w:r>
- <w:t xml:space="preserve">Video </w:t>
- </w:r>
- <w:del>
- <w:r>
- <w:delText xml:space="preserve">provides </w:delText>
- </w:r>
- </w:del>
- <w:r>
- <w:t>a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.del &&
- parent.Name == W.p)
- {
- return new XElement(W.ins,
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted paragraph mark
- #if false
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T21:52:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:r>
- <w:t>Video provides a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- <w:p>
- <w:r>
- <w:t>You can also type a keyword to search online for the video that best fits your document.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.del &&
- parent.Name == W.rPr &&
- parent.Parent.Name == W.pPr)
- {
- return new XElement(W.ins);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Inserted paragraph mark
- #if false
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T21:58:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:r>
- <w:t xml:space="preserve">Video provides a powerful way to help you prove your point. </w:t>
- </w:r>
- </w:p>
- <w:p>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.ins &&
- parent.Name == W.rPr &&
- parent.Parent.Name == W.pPr)
- {
- return new XElement(W.del);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Inserted run
- #if false
- <w:p>
- <w:r>
- <w:t xml:space="preserve">Video </w:t>
- </w:r>
- <w:ins>
- <w:r>
- <w:t xml:space="preserve">provides </w:t>
- </w:r>
- </w:ins>
- <w:r>
- <w:t>a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.ins &&
- parent.Name == W.p)
- {
- var newRri = new ReverseRevisionsInfo() { InInsert = true };
- return new XElement(W.del,
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted table row
- #if false
- <w:tbl>
- <w:tr>
- <w:tc>
- <w:p>
- <w:r>
- <w:t>1</w:t>
- </w:r>
- </w:p>
- </w:tc>
- </w:tr>
- <w:tr>
- <w:trPr>
- <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T22:15:00Z"/>
- </w:trPr>
- <w:tc>
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:del w:id="1" w:author="Eric White" w:date="2017-03-24T22:15:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:del w:id="2" w:author="Eric White" w:date="2017-03-24T22:15:00Z">
- <w:r>
- <w:delText>4</w:delText>
- </w:r>
- </w:del>
- </w:p>
- </w:tc>
- </w:tr>
- <w:tr>
- <w:tc>
- <w:p>
- <w:r>
- <w:t>7</w:t>
- </w:r>
- </w:p>
- </w:tc>
- </w:tr>
- </w:tbl>
- #endif
- if (element.Name == W.del &&
- parent.Name == W.trPr)
- {
- return new XElement(W.ins);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Inserted table row
- #if false
- <w:tbl>
- <w:tr>
- <w:tc>
- <w:p>
- <w:r>
- <w:t>1</w:t>
- </w:r>
- </w:p>
- </w:tc>
- </w:tr>
- <w:tr>
- <w:trPr>
- <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T22:16:00Z"/>
- </w:trPr>
- <w:tc>
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:ins w:id="1" w:author="Eric White" w:date="2017-03-24T22:16:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:ins w:id="2" w:author="Eric White" w:date="2017-03-24T22:16:00Z">
- <w:r>
- <w:t>1a</w:t>
- </w:r>
- </w:ins>
- </w:p>
- </w:tc>
- </w:tr>
- <w:tr>
- <w:tc>
- <w:p>
- <w:r>
- <w:t>4</w:t>
- </w:r>
- </w:p>
- </w:tc>
- </w:tr>
- </w:tbl>
- #endif
- if (element.Name == W.ins &&
- parent.Name == W.trPr)
- {
- return new XElement(W.del);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted math control character
- #if false
- <w:p w:rsidR="007F4E48" w:rsidRDefault="00C9403B">
- <m:oMathPara>
- <m:oMath>
- <m:r>
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>A=</m:t>
- </m:r>
- <m:r>
- <w:del w:id="0" w:author="Eric White" w:date="2017-03-24T22:53:00Z">
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>2</m:t>
- </w:del>
- </m:r>
- <m:r>
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>π</m:t>
- </m:r>
- #endif
- if (element.Name == W.del &&
- parent.Name == M.r)
- {
- return new XElement(W.ins,
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Inserted math control character
- #if false
- <w:p w:rsidR="007F4E48" w:rsidRDefault="00C9403B">
- <m:oMathPara>
- <m:oMath>
- <m:r>
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>A=</m:t>
- </m:r>
- <m:r>
- <w:ins w:id="0" w:author="Eric White" w:date="2017-03-24T22:54:00Z">
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>2</m:t>
- </w:ins>
- </m:r>
- <m:r>
- <w:rPr>
- <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
- </w:rPr>
- <m:t>π</m:t>
- </m:r>
- #endif
- if (element.Name == W.ins &&
- parent.Name == M.r)
- {
- return new XElement(W.del,
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // moveFrom / moveTo
- #if false
- <w:p>
- <w:r>
- <w:t>Video provides a powerful way.</w:t>
- </w:r>
- </w:p>
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:moveFrom w:id="0" w:author="Eric White" w:date="2017-03-24T23:18:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:moveFromRangeStart w:id="1" w:author="Eric White" w:date="2017-03-24T23:18:00Z" w:name="move478160808"/>
- <w:moveFrom w:id="2" w:author="Eric White" w:date="2017-03-24T23:18:00Z">
- <w:r>
- <w:t>When you click Online Video.</w:t>
- </w:r>
- </w:moveFrom>
- </w:p>
- <w:moveFromRangeEnd w:id="1"/>
- <w:p>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>You can also type a keyword.</w:t>
- </w:r>
- </w:p>
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:moveTo w:id="3" w:author="Eric White" w:date="2017-03-24T23:18:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:moveToRangeStart w:id="5" w:author="Eric White" w:date="2017-03-24T23:18:00Z" w:name="move478160808"/>
- <w:moveTo w:id="6" w:author="Eric White" w:date="2017-03-24T23:18:00Z">
- <w:r>
- <w:t>When you click Online Video.</w:t>
- </w:r>
- </w:moveTo>
- </w:p>
- <w:moveToRangeEnd w:id="5"/>
- <w:p>
- <w:r>
- <w:t>Make your document look professionally produced.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.moveFrom)
- {
- return new XElement(W.moveTo,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.moveFromRangeStart)
- {
- return new XElement(W.moveToRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.moveFromRangeEnd)
- {
- return new XElement(W.moveToRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.moveTo)
- {
- return new XElement(W.moveFrom,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.moveToRangeStart)
- {
- return new XElement(W.moveFromRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.moveToRangeEnd)
- {
- return new XElement(W.moveFromRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted content control
- #if false
- <w:p>
- <w:customXmlDelRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
- <w:sdt>
- <w:sdtPr>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:id w:val="990292373"/>
- <w:placeholder>
- <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
- </w:placeholder>
- <w:text/>
- </w:sdtPr>
- <w:sdtContent>
- <w:customXmlDelRangeEnd w:id="1"/>
- <w:r>
- <w:t>Video</w:t>
- </w:r>
- <w:customXmlDelRangeStart w:id="2" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
- </w:sdtContent>
- </w:sdt>
- <w:customXmlDelRangeEnd w:id="2"/>
- <w:r>
- <w:t xml:space="preserve"> provides a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.customXmlDelRangeStart)
- {
- return new XElement(W.customXmlInsRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.customXmlDelRangeEnd)
- {
- return new XElement(W.customXmlInsRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Inserted content control
- #if false
- <w:p>
- <w:customXmlInsRangeStart w:id="0" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
- <w:sdt>
- <w:sdtPr>
- <w:id w:val="-473839966"/>
- <w:placeholder>
- <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
- </w:placeholder>
- <w:text/>
- </w:sdtPr>
- <w:sdtContent>
- <w:customXmlInsRangeEnd w:id="0"/>
- <w:r>
- <w:t>Video</w:t>
- </w:r>
- <w:customXmlInsRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:10:00Z"/>
- </w:sdtContent>
- </w:sdt>
- <w:customXmlInsRangeEnd w:id="1"/>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t xml:space="preserve"> provides a powerful way to help you prove your point.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.customXmlInsRangeStart)
- {
- return new XElement(W.customXmlDelRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.customXmlInsRangeEnd)
- {
- return new XElement(W.customXmlDelRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Moved content control
- #if false
- <w:p>
- <w:r>
- <w:t>Video provides a powerful way.</w:t>
- </w:r>
- </w:p>
- <w:customXmlMoveFromRangeStart w:id="0" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- <w:moveFromRangeStart w:id="1" w:author="Eric White" w:date="2017-03-25T22:21:00Z" w:name="move478243824" w:displacedByCustomXml="next"/>
- <w:sdt>
- <w:sdtPr>
- <w:id w:val="-2060007328"/>
- <w:placeholder>
- <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
- </w:placeholder>
- </w:sdtPr>
- <w:sdtContent>
- <w:customXmlMoveFromRangeEnd w:id="0"/>
- <w:p w:rsidR="00D306FD" w:rsidDel="001037E6" w:rsidRDefault="00D306FD">
- <w:pPr>
- <w:rPr>
- <w:moveFrom w:id="2" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- <w:lang w:val="en-US"/>
- </w:rPr>
- </w:pPr>
- <w:moveFrom w:id="3" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
- <w:r w:rsidDel="001037E6">
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>When you click Online Video.</w:t>
- </w:r>
- </w:moveFrom>
- </w:p>
- <w:customXmlMoveFromRangeStart w:id="4" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- </w:sdtContent>
- </w:sdt>
- <w:customXmlMoveFromRangeEnd w:id="4"/>
- <w:moveFromRangeEnd w:id="1"/>
- <w:p>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>You can also type a keyword.</w:t>
- </w:r>
- </w:p>
- <w:p>
- <w:r>
- <w:rPr>
- <w:lang w:val="en-US"/>
- </w:rPr>
- <w:t>To make your document look.</w:t>
- </w:r>
- </w:p>
- <w:customXmlMoveToRangeStart w:id="5" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- <w:moveToRangeStart w:id="6" w:author="Eric White" w:date="2017-03-25T22:21:00Z" w:name="move478243824" w:displacedByCustomXml="next"/>
- <w:sdt>
- <w:sdtPr>
- <w:id w:val="-483622649"/>
- <w:placeholder>
- <w:docPart w:val="DC46F197491D4EC8B79DB4CE2D22E222"/>
- </w:placeholder>
- </w:sdtPr>
- <w:sdtContent>
- <w:customXmlMoveToRangeEnd w:id="5"/>
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:moveTo w:id="8" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:moveTo w:id="9" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
- <w:r>
- <w:t>When you click Online Video.</w:t>
- </w:r>
- </w:moveTo>
- </w:p>
- <w:customXmlMoveToRangeStart w:id="10" w:author="Eric White" w:date="2017-03-25T22:21:00Z"/>
- </w:sdtContent>
- </w:sdt>
- <w:customXmlMoveToRangeEnd w:id="10"/>
- <w:moveToRangeEnd w:id="6"/>
- <w:p>
- <w:ins w:id="11" w:author="Eric White" w:date="2017-03-25T22:21:00Z">
- <w:r>
- <w:t xml:space="preserve"> </w:t>
- </w:r>
- </w:ins>
- <w:r>
- <w:t>For example, you can add.</w:t>
- </w:r>
- </w:p>
- #endif
- if (element.Name == W.customXmlMoveFromRangeStart)
- {
- return new XElement(W.customXmlMoveToRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.customXmlMoveFromRangeEnd)
- {
- return new XElement(W.customXmlMoveToRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.customXmlMoveToRangeStart)
- {
- return new XElement(W.customXmlMoveFromRangeStart,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- if (element.Name == W.customXmlMoveToRangeEnd)
- {
- return new XElement(W.customXmlMoveFromRangeEnd,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Deleted field code
- #if false
- <w:p>
- <w:pPr>
- <w:rPr>
- <w:del w:id="0" w:author="Eric White" w:date="2017-03-25T22:43:00Z"/>
- </w:rPr>
- </w:pPr>
- <w:del w:id="1" w:author="Eric White" w:date="2017-03-25T22:43:00Z">
- <w:r>
- <w:fldChar w:fldCharType="begin"/>
- </w:r>
- <w:r>
- <w:delInstrText xml:space="preserve"> D</w:delInstrText>
- </w:r>
- <w:r>
- <w:rPr>
- <w:color w:val="FF0000"/>
- </w:rPr>
- <w:delInstrText>A</w:delInstrText>
- </w:r>
- <w:r>
- <w:delInstrText xml:space="preserve">TE </w:delInstrText>
- </w:r>
- <w:r>
- <w:fldChar w:fldCharType="separate"/>
- </w:r>
- <w:r>
- <w:delText>25/03/2017</w:delText>
- </w:r>
- <w:r>
- <w:fldChar w:fldCharType="end"/>
- </w:r>
- </w:del>
- </w:p>
- #endif
- if (element.Name == W.delInstrText)
- {
- return new XElement(W.instrText,
- element.Attributes(), // pulls in xml:space attribute
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Change inserted instrText element to w:delInstrText
- if (element.Name == W.instrText && rri.InInsert)
- {
- return new XElement(W.delInstrText,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Change inserted text element to w:delText
- if (element.Name == W.t && rri.InInsert)
- {
- return new XElement(W.delText,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Change w:delText to w:t
- if (element.Name == W.delText)
- {
- return new XElement(W.t,
- element.Attributes(), // pulls in xml:space attribute
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Identity transform
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => ReverseRevisionsTransform(n, rri)));
- }
- return node;
- }
- public static WmlDocument AcceptRevisions(WmlDocument document)
- {
- using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
- {
- using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
- {
- AcceptRevisions(doc);
- }
- return streamDoc.GetModifiedWmlDocument();
- }
- }
- public static void AcceptRevisions(WordprocessingDocument doc)
- {
- AcceptRevisionsForPart(doc.MainDocumentPart);
- foreach (var part in doc.MainDocumentPart.HeaderParts)
- AcceptRevisionsForPart(part);
- foreach (var part in doc.MainDocumentPart.FooterParts)
- AcceptRevisionsForPart(part);
- if (doc.MainDocumentPart.EndnotesPart != null)
- AcceptRevisionsForPart(doc.MainDocumentPart.EndnotesPart);
- if (doc.MainDocumentPart.FootnotesPart != null)
- AcceptRevisionsForPart(doc.MainDocumentPart.FootnotesPart);
- if (doc.MainDocumentPart.StyleDefinitionsPart != null)
- AcceptRevisionsForStylesDefinitionPart(doc.MainDocumentPart.StyleDefinitionsPart);
- }
- private static void AcceptRevisionsForStylesDefinitionPart(StyleDefinitionsPart stylesDefinitionsPart)
- {
- var xDoc = stylesDefinitionsPart.GetXDocument();
- var newRoot = AcceptRevisionsForStylesTransform(xDoc.Root);
- xDoc.Root.ReplaceWith(newRoot);
- stylesDefinitionsPart.PutXDocument();
- }
- private static object AcceptRevisionsForStylesTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.pPrChange || element.Name == W.rPrChange)
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptRevisionsForStylesTransform(n)));
- }
- return node;
- }
- public static void AcceptRevisionsForPart(OpenXmlPart part)
- {
- XElement documentElement = part.GetXDocument().Root;
- documentElement = (XElement)RemoveRsidTransform(documentElement);
- documentElement = (XElement)FixUpDeletedOrInsertedFieldCodesTransform(documentElement);
- var containsMoveFromMoveTo = documentElement.Descendants(W.moveFrom).Any();
- documentElement = (XElement)AcceptMoveFromMoveToTransform(documentElement);
- documentElement = AcceptMoveFromRanges(documentElement);
- // AcceptParagraphEndTagsInMoveFromTransform needs rewritten similar to AcceptDeletedAndMoveFromParagraphMarks
- documentElement = (XElement)AcceptParagraphEndTagsInMoveFromTransform(documentElement);
- documentElement = AcceptDeletedAndMovedFromContentControls(documentElement);
- documentElement = AcceptDeletedAndMoveFromParagraphMarks(documentElement);
- if (containsMoveFromMoveTo)
- documentElement = (XElement)RemoveRowsLeftEmptyByMoveFrom(documentElement);
- documentElement = (XElement)AcceptAllOtherRevisionsTransform(documentElement);
- documentElement = (XElement)AcceptDeletedCellsTransform(documentElement);
- documentElement = (XElement)MergeAdjacentTablesTransform(documentElement);
- documentElement = (XElement)AddEmptyParagraphToAnyEmptyCells(documentElement);
- documentElement.Descendants().Attributes().Where(a => a.Name == PT.UniqueId || a.Name == PT.RunIds).Remove();
- documentElement.Descendants(W.numPr).Where(np => !np.HasElements).Remove();
- XDocument newXDoc = new XDocument(documentElement);
- part.PutXDocument(newXDoc);
- }
- // Note that AcceptRevisionsForElement is an incomplete implementation. It is not possible to accept all varieties of revisions
- // for a single paragraph. The paragraph may contain a marker for a deleted or inserted content control, as one example, of
- // which there are many. This method accepts simple revisions, such as deleted or inserted text, which is the most common use
- // case.
- public static XElement AcceptRevisionsForElement(XElement element)
- {
- XElement rElement = element;
- rElement = (XElement)RemoveRsidTransform(rElement);
- var containsMoveFromMoveTo = rElement.Descendants(W.moveFrom).Any();
- rElement = (XElement)AcceptMoveFromMoveToTransform(rElement);
- rElement = (XElement)AcceptAllOtherRevisionsTransform(rElement);
- rElement.Descendants().Attributes().Where(a => a.Name == PT.UniqueId || a.Name == PT.RunIds).Remove();
- rElement.Descendants(W.numPr).Where(np => !np.HasElements).Remove();
- return rElement;
- }
- private static object FixUpDeletedOrInsertedFieldCodesTransform(XNode node)
- {
- var element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.p)
- {
- // 1 other
- // 2 w:del/w:r/w:fldChar
- // 3 w:ins/w:r/w:fldChar
- // 4 w:instrText
- // 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.
- // transform 1, 2, 3 as usual
- var groupedParaContentsKey = element.Elements().Select(e =>
- {
- if (e.Name == W.del && e.Elements(W.r).Elements(W.fldChar).Any())
- return 2;
- if (e.Name == W.ins && e.Elements(W.r).Elements(W.fldChar).Any())
- return 3;
- if (e.Name == W.r && e.Element(W.instrText) != null)
- return 4;
- return 1;
- });
- var zipped = element.Elements().Zip(groupedParaContentsKey, (e, k) => new { Ele = e, Key = k });
- var grouped = zipped.GroupAdjacent(z => z.Key).ToArray();
- var gLen = grouped.Length;
- //if (gLen != 1)
- // Console.WriteLine();
- var newParaContents = grouped
- .Select((g, i) =>
- {
- if (g.Key == 1 || g.Key == 2 || g.Key == 3)
- return (object)g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
- if (g.Key == 4)
- {
- if (i == 0 || i == gLen - 1)
- return g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
- if (grouped[i-1].Key == 2 &&
- grouped[i+1].Key == 2)
- {
- return new XElement(W.del,
- g.Select(gc => TransformInstrTextToDelInstrText(gc.Ele)));
- }
- else if (grouped[i - 1].Key == 3 &&
- grouped[i + 1].Key == 3)
- {
- return new XElement(W.ins,
- g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele)));
- }
- else
- {
- return g.Select(gc => FixUpDeletedOrInsertedFieldCodesTransform(gc.Ele));
- }
- }
- throw new OpenXmlPowerToolsException("Internal error");
- });
- var newParagraph = new XElement(W.p,
- element.Attributes(),
- newParaContents);
- return newParagraph;
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => FixUpDeletedOrInsertedFieldCodesTransform(n)));
- }
- return node;
- }
- private static object TransformInstrTextToDelInstrText(XNode node)
- {
- var element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.instrText)
- return new XElement(W.delInstrText,
- element.Attributes(),
- element.Nodes());
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => TransformInstrTextToDelInstrText(n)));
- }
- return node;
- }
- private static object AddEmptyParagraphToAnyEmptyCells(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.tc && !element.Elements().Where(e => e.Name != W.tcPr).Any())
- return new XElement(W.tc,
- element.Attributes(),
- element.Elements(),
- new XElement(W.p));
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AddEmptyParagraphToAnyEmptyCells(n)));
- }
- return node;
- }
- private static Dictionary<XName, int> Order_tcPr = new Dictionary<XName, int>
- {
- { W.cnfStyle, 10 },
- { W.tcW, 20 },
- { W.gridSpan, 30 },
- { W.hMerge, 40 },
- { W.vMerge, 50 },
- { W.tcBorders, 60 },
- { W.shd, 70 },
- { W.noWrap, 80 },
- { W.tcMar, 90 },
- { W.textDirection, 100 },
- { W.tcFitText, 110 },
- { W.vAlign, 120 },
- { W.hideMark, 130 },
- { W.headers, 140 },
- };
- private static XElement FixWidths(XElement tbl)
- {
- var newTbl = new XElement(tbl);
- var gridLines = tbl.Elements(W.tblGrid).Elements(W.gridCol).Attributes(W._w).Select(w => (int)w).ToArray();
- foreach (var tr in newTbl.Elements(W.tr))
- {
- int used = 0;
- int lastUsed = -1;
- foreach (var tc in tr.Elements(W.tc))
- {
- var tcW = tc.Elements(W.tcPr).Elements(W.tcW).Attributes(W._w).FirstOrDefault();
- if (tcW != null)
- {
- int? gridSpan = (int?)tc.Elements(W.tcPr).Elements(W.gridSpan).Attributes(W.val).FirstOrDefault();
- if (gridSpan == null)
- gridSpan = 1;
-
- var z = Math.Min(gridLines.Length - 1, lastUsed + (int)gridSpan);
- int w = gridLines.Where((g, i) => i > lastUsed && i <= z).Sum();
- tcW.Value = w.ToString();
- lastUsed += (int)gridSpan;
- used += (int)gridSpan;
- }
- }
- }
- return newTbl;
- }
- private static object AcceptMoveFromMoveToTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.moveTo)
- return element.Nodes().Select(n => AcceptMoveFromMoveToTransform(n));
- if (element.Name == W.moveFrom)
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptMoveFromMoveToTransform(n)));
- }
- return node;
- }
- private static XElement AcceptMoveFromRanges(XElement document)
- {
- string wordProcessingNamespacePrefix = document.GetPrefixOfNamespace(W.w);
- // The following lists contain the elements that are between start/end elements.
- List<XElement> startElementTagsInMoveFromRange = new List<XElement>();
- List<XElement> endElementTagsInMoveFromRange = new List<XElement>();
- // Following are the elements that *may* be in a range that has both start and end
- // elements.
- Dictionary<string, PotentialInRangeElements> potentialDeletedElements =
- new Dictionary<string, PotentialInRangeElements>();
- foreach (var tag in DescendantAndSelfTags(document))
- {
- if (tag.Element.Name == W.moveFromRangeStart)
- {
- string id = tag.Element.Attribute(W.id).Value;
- potentialDeletedElements.Add(id, new PotentialInRangeElements());
- continue;
- }
- if (tag.Element.Name == W.moveFromRangeEnd)
- {
- string id = tag.Element.Attribute(W.id).Value;
- if (potentialDeletedElements.ContainsKey(id))
- {
- startElementTagsInMoveFromRange.AddRange(
- potentialDeletedElements[id].PotentialStartElementTagsInRange);
- endElementTagsInMoveFromRange.AddRange(
- potentialDeletedElements[id].PotentialEndElementTagsInRange);
- potentialDeletedElements.Remove(id);
- }
- continue;
- }
- if (potentialDeletedElements.Count > 0)
- {
- if (tag.TagType == TagTypeEnum.Element &&
- (tag.Element.Name != W.moveFromRangeStart &&
- tag.Element.Name != W.moveFromRangeEnd))
- {
- foreach (var id in potentialDeletedElements)
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- continue;
- }
- if (tag.TagType == TagTypeEnum.EmptyElement &&
- (tag.Element.Name != W.moveFromRangeStart &&
- tag.Element.Name != W.moveFromRangeEnd))
- {
- foreach (var id in potentialDeletedElements)
- {
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- }
- continue;
- }
- if (tag.TagType == TagTypeEnum.EndElement &&
- (tag.Element.Name != W.moveFromRangeStart &&
- tag.Element.Name != W.moveFromRangeEnd))
- {
- foreach (var id in potentialDeletedElements)
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- continue;
- }
- }
- }
- var moveFromElementsToDelete = startElementTagsInMoveFromRange
- .Intersect(endElementTagsInMoveFromRange)
- .ToArray();
- if (moveFromElementsToDelete.Count() > 0)
- return (XElement)AcceptMoveFromRangesTransform(
- document, moveFromElementsToDelete);
- return document;
- }
- private enum MoveFromCollectionType
- {
- ParagraphEndTagInMoveFromRange,
- Other
- };
- private static object AcceptParagraphEndTagsInMoveFromTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (W.BlockLevelContentContainers.Contains(element.Name))
- {
- var groupedBodyChildren = element
- .Elements()
- .GroupAdjacent(c =>
- {
- BlockContentInfo pi = c.GetParagraphInfo();
- if (pi.ThisBlockContentElement != null)
- {
- bool paragraphMarkIsInMoveFromRange =
- pi.ThisBlockContentElement.Elements(W.moveFromRangeStart).Any() &&
- !pi.ThisBlockContentElement.Elements(W.moveFromRangeEnd).Any();
- if (paragraphMarkIsInMoveFromRange)
- return MoveFromCollectionType.ParagraphEndTagInMoveFromRange;
- }
- XElement previousContentElement = c.ContentElementsBeforeSelf()
- .Where(e => e.GetParagraphInfo().ThisBlockContentElement != null)
- .FirstOrDefault();
- if (previousContentElement != null)
- {
- BlockContentInfo pi2 = previousContentElement.GetParagraphInfo();
- if (c.Name == W.p &&
- pi2.ThisBlockContentElement.Elements(W.moveFromRangeStart).Any() &&
- !pi2.ThisBlockContentElement.Elements(W.moveFromRangeEnd).Any())
- return MoveFromCollectionType.ParagraphEndTagInMoveFromRange;
- }
- return MoveFromCollectionType.Other;
- })
- .ToList();
- // If there is only one group, and it's key is MoveFromCollectionType.Other
- // then there is nothing to do.
- if (groupedBodyChildren.Count() == 1 &&
- groupedBodyChildren.First().Key == MoveFromCollectionType.Other)
- {
- XElement newElement = new XElement(element.Name,
- element.Attributes(),
- groupedBodyChildren.Select(g =>
- {
- if (g.Key == MoveFromCollectionType.Other)
- return (object)g;
- // This is a transform that produces the first element in the
- // collection, except that the paragraph in the descendents is
- // replaced with a new paragraph that contains all contents of the
- // existing paragraph, plus subsequent elements in the group
- // collection, where the paragraph in each of those groups is
- // collapsed.
- return CoalesqueParagraphEndTagsInMoveFromTransform(g.First(), g);
- }));
- return newElement;
- }
- else
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n =>
- AcceptParagraphEndTagsInMoveFromTransform(n)));
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptParagraphEndTagsInMoveFromTransform(n)));
- }
- return node;
- }
- private static object AcceptAllOtherRevisionsTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- /// Accept inserted text, inserted paragraph marks, etc.
- /// Collapse all w:ins elements.
- if (element.Name == W.ins)
- return element
- .Nodes()
- .Select(n => AcceptAllOtherRevisionsTransform(n));
- /// Remove all of the following elements. These elements are processed in:
- /// AcceptDeletedAndMovedFromContentControls
- /// AcceptMoveFromMoveToTransform
- /// AcceptDeletedAndMoveFromParagraphMarksTransform
- /// AcceptParagraphEndTagsInMoveFromTransform
- /// AcceptMoveFromRanges
- if (element.Name == W.customXmlDelRangeStart ||
- element.Name == W.customXmlDelRangeEnd ||
- element.Name == W.customXmlInsRangeStart ||
- element.Name == W.customXmlInsRangeEnd ||
- element.Name == W.customXmlMoveFromRangeStart ||
- element.Name == W.customXmlMoveFromRangeEnd ||
- element.Name == W.customXmlMoveToRangeStart ||
- element.Name == W.customXmlMoveToRangeEnd ||
- element.Name == W.moveFromRangeStart ||
- element.Name == W.moveFromRangeEnd ||
- element.Name == W.moveToRangeStart ||
- element.Name == W.moveToRangeEnd)
- return null;
- /// Accept revisions in formatting on paragraphs.
- /// Accept revisions in formatting on runs.
- /// Accept revisions for applied styles to a table.
- /// Accept revisions for grid revisions to a table.
- /// Accept revisions for column properties.
- /// Accept revisions for row properties.
- /// Accept revisions for table level property exceptions.
- /// Accept revisions for section properties.
- /// Accept numbering revision in fields.
- /// Accept deleted field code text.
- /// Accept deleted literal text.
- /// Accept inserted cell.
- if (element.Name == W.pPrChange ||
- element.Name == W.rPrChange ||
- element.Name == W.tblPrChange ||
- element.Name == W.tblGridChange ||
- element.Name == W.tcPrChange ||
- element.Name == W.trPrChange ||
- element.Name == W.tblPrExChange ||
- element.Name == W.sectPrChange ||
- element.Name == W.numberingChange ||
- element.Name == W.delInstrText ||
- element.Name == W.delText ||
- element.Name == W.cellIns)
- return null;
- // Accept revisions for deleted math control character.
- // Match m:f/m:fPr/m:ctrlPr/w:del, remove m:f.
- if (element.Name == M.f &&
- element.Elements(M.fPr).Elements(M.ctrlPr).Elements(W.del).Any())
- return null;
- // Accept revisions for deleted rows in tables.
- // Match w:tr/w:trPr/w:del, remove w:tr.
- if (element.Name == W.tr &&
- element.Elements(W.trPr).Elements(W.del).Any())
- return null;
- // Accept deleted text in paragraphs.
- if (element.Name == W.del)
- return null;
- // Accept revisions for vertically merged cells.
- // cellMerge with a parent of tcPr, with attribute w:vMerge="rest" transformed
- // to <w:vMerge w:val="restart"/>
- // cellMerge with a parent of tcPr, with attribute w:vMerge="cont" transformed
- // to <w:vMerge w:val="continue"/>
- if (element.Name == W.cellMerge &&
- element.Parent.Name == W.tcPr &&
- (string)element.Attribute(W.vMerge) == "rest")
- return new XElement(W.vMerge,
- new XAttribute(W.val, "restart"));
- if (element.Name == W.cellMerge &&
- element.Parent.Name == W.tcPr &&
- (string)element.Attribute(W.vMerge) == "cont")
- return new XElement(W.vMerge,
- new XAttribute(W.val, "continue"));
- // Otherwise do identity clone.
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptAllOtherRevisionsTransform(n)));
- }
- return node;
- }
- private static object CollapseParagraphTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.p)
- return element.Elements().Where(e => e.Name != W.pPr);
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CollapseParagraphTransform(n)));
- }
- return node;
- }
- private enum DeletedParagraphCollectionType
- {
- DeletedParagraphMarkContent,
- ParagraphFollowing,
- Other
- };
- /// Accept deleted paragraphs.
- ///
- /// Group together all paragraphs that contain w:p/w:pPr/w:rPr/w:del elements. Make a
- /// second group for the content element immediately following a paragraph that contains
- /// a w:del element. The code uses the approach of dealing with paragraph content at
- /// 'levels', ignoring paragraph content at other levels. Form a new paragraph that
- /// contains the content of the grouped paragraphs with deleted paragraph marks, and the
- /// content of the paragraph immediately following a paragraph that contains a deleted
- /// paragraph mark. Include in the new paragraph the paragraph properties from the
- /// paragraph following. When assembling the new paragraph, use a transform that collapses
- /// the paragraph nodes when adding content, thereby preserving custom XML and content
- /// controls.
- private static void AnnotateBlockContentElements(XElement contentContainer)
- {
- // For convenience, there is a ParagraphInfo annotation on the contentContainer.
- // It contains the same information as the ParagraphInfo annotation on the first
- // paragraph.
- if (contentContainer.Annotation<BlockContentInfo>() != null)
- return;
- XElement firstContentElement = contentContainer
- .Elements()
- .DescendantsAndSelf()
- .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl);
- if (firstContentElement == null)
- return;
- // Add the annotation on the contentContainer.
- BlockContentInfo currentContentInfo = new BlockContentInfo()
- {
- PreviousBlockContentElement = null,
- ThisBlockContentElement = firstContentElement,
- NextBlockContentElement = null
- };
- // Add as annotation even though NextParagraph is not set yet.
- contentContainer.AddAnnotation(currentContentInfo);
- while (true)
- {
- currentContentInfo.ThisBlockContentElement.AddAnnotation(currentContentInfo);
- // Find next sibling content element.
- XElement nextContentElement = null;
- XElement current = currentContentInfo.ThisBlockContentElement;
- while (true)
- {
- nextContentElement = current
- .ElementsAfterSelf()
- .DescendantsAndSelf()
- .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl);
- if (nextContentElement != null)
- {
- currentContentInfo.NextBlockContentElement = nextContentElement;
- break;
- }
- current = current.Parent;
- // When we've backed up the tree to the contentContainer, we're done.
- if (current == contentContainer)
- return;
- }
- currentContentInfo = new BlockContentInfo()
- {
- PreviousBlockContentElement = currentContentInfo.ThisBlockContentElement,
- ThisBlockContentElement = nextContentElement,
- NextBlockContentElement = null
- };
- }
- }
- private static IEnumerable<BlockContentInfo> IterateBlockContentElements(XElement element)
- {
- XElement current = element.Elements().FirstOrDefault();
- if (current == null)
- yield break;
- AnnotateBlockContentElements(element);
- BlockContentInfo currentBlockContentInfo = element.Annotation<BlockContentInfo>();
- if (currentBlockContentInfo != null)
- {
- while (true)
- {
- yield return currentBlockContentInfo;
- if (currentBlockContentInfo.NextBlockContentElement == null)
- yield break;
- currentBlockContentInfo = currentBlockContentInfo.NextBlockContentElement.Annotation<BlockContentInfo>();
- }
- }
- }
- public static class PT
- {
- public static XNamespace pt = "http://www.codeplex.com/PowerTools/2009/RevisionAccepter";
- public static XName UniqueId = pt + "UniqueId";
- public static XName RunIds = pt + "RunIds";
- }
- private static void AnnotateRunElementsWithId(XElement element)
- {
- int runId = 0;
- foreach (XElement e in element.Descendants().Where(e => e.Name == W.r))
- {
- if (e.Name == W.r)
- e.Add(new XAttribute(PT.UniqueId, runId++));
- }
- }
- private static void AnnotateContentControlsWithRunIds(XElement element)
- {
- int sdtId = 0;
- foreach (XElement e in element.Descendants(W.sdt))
- {
- // old version
- //e.Add(new XAttribute(PT.RunIds,
- // e.Descendants(W.r).Select(r => r.Attribute(PT.UniqueId).Value).StringConcatenate(s => s + ",").Trim(',')),
- // new XAttribute(PT.UniqueId, sdtId++));
- e.Add(new XAttribute(PT.RunIds,
- e.DescendantsTrimmed(W.txbxContent)
- .Where(d => d.Name == W.r)
- .Select(r => r.Attribute(PT.UniqueId).Value)
- .StringConcatenate(s => s + ",")
- .Trim(',')),
- new XAttribute(PT.UniqueId, sdtId++));
- }
- }
- private static XElement AddBlockLevelContentControls(XElement newDocument, XElement original)
- {
- var originalContentControls = original.Descendants(W.sdt).ToList();
- var existingContentControls = newDocument.Descendants(W.sdt).ToList();
- var contentControlsToAdd = originalContentControls
- .Select(occ => occ.Attribute(PT.UniqueId).Value)
- .Except(existingContentControls
- .Select(ecc => ecc.Attribute(PT.UniqueId).Value));
- foreach (var contentControl in originalContentControls
- .Where(occ => contentControlsToAdd.Contains(occ.Attribute(PT.UniqueId).Value)))
- {
- // TODO - Need a slight modification here. If there is a paragraph
- // in the content control that contains no runs, then the paragraph isn't included in the
- // content control, because the following triggers off of runs.
- // To see an example of this, see example document "NumberingParagraphPropertiesChange.docxs"
- // find list of runs to surround
- var runIds = contentControl.Attribute(PT.RunIds).Value.Split(',');
- var runs = contentControl.Descendants(W.r).Where(r => runIds.Contains(r.Attribute(PT.UniqueId).Value));
- // find the runs in the new document
- var runsInNewDocument = runs.Select(r => newDocument.Descendants(W.r).First(z => z.Attribute(PT.UniqueId).Value == r.Attribute(PT.UniqueId).Value)).ToList();
- // find common ancestor
- List<XElement> runAncestorIntersection = null;
- foreach (var run in runsInNewDocument)
- {
- if (runAncestorIntersection == null)
- runAncestorIntersection = run.Ancestors().ToList();
- else
- runAncestorIntersection = run.Ancestors().Intersect(runAncestorIntersection).ToList();
- }
- if (runAncestorIntersection == null)
- continue;
- XElement commonAncestor = runAncestorIntersection.InDocumentOrder().Last();
- // find child of common ancestor that contains first run
- // find child of common ancestor that contains last run
- // create new common ancestor:
- // elements before first run child
- // add content control, and runs from first run child to last run child
- // elements after last run child
- var firstRunChild = commonAncestor
- .Elements()
- .First(c => c.DescendantsAndSelf()
- .Any(z => z.Name == W.r &&
- z.Attribute(PT.UniqueId).Value == runsInNewDocument.First().Attribute(PT.UniqueId).Value));
- var lastRunChild = commonAncestor
- .Elements()
- .First(c => c.DescendantsAndSelf()
- .Any(z => z.Name == W.r &&
- z.Attribute(PT.UniqueId).Value == runsInNewDocument.Last().Attribute(PT.UniqueId).Value));
- /// If the list of runs for the content control is exactly the list of runs for the paragraph, then
- /// create the content control surrounding the paragraph, not surrounding the runs.
- if (commonAncestor.Name == W.p &&
- commonAncestor.Elements()
- .Where(e => e.Name != W.pPr &&
- e.Name != W.commentRangeStart &&
- e.Name != W.commentRangeEnd)
- .FirstOrDefault() == firstRunChild &&
- commonAncestor.Elements()
- .Where(e => e.Name != W.pPr &&
- e.Name != W.commentRangeStart &&
- e.Name != W.commentRangeEnd)
- .LastOrDefault() == lastRunChild)
- {
- // replace commonAncestor with content control containing commonAncestor
- XElement newContentControl = new XElement(contentControl.Name,
- contentControl.Attributes(),
- contentControl.Elements().Where(e => e.Name != W.sdtContent),
- new XElement(W.sdtContent, commonAncestor));
- XElement newContentControlOrdered = new XElement(contentControl.Name,
- contentControl.Attributes(),
- contentControl.Elements().OrderBy(e =>
- {
- if (Order_sdt.ContainsKey(e.Name))
- return Order_sdt[e.Name];
- return 999;
- }));
- commonAncestor.ReplaceWith(newContentControlOrdered);
- continue;
- }
- List<XElement> elementsBeforeRange = commonAncestor
- .Elements()
- .TakeWhile(e => e != firstRunChild)
- .ToList();
- List<XElement> elementsInRange = commonAncestor
- .Elements()
- .SkipWhile(e => e != firstRunChild)
- .TakeWhile(e => e != lastRunChild.ElementsAfterSelf().FirstOrDefault())
- .ToList();
- List<XElement> elementsAfterRange = commonAncestor
- .Elements()
- .SkipWhile(e => e != lastRunChild.ElementsAfterSelf().FirstOrDefault())
- .ToList();
- // detatch from current parent
- commonAncestor.Elements().Remove();
- XElement newContentControl2 = new XElement(contentControl.Name,
- contentControl.Attributes(),
- contentControl.Elements().Where(e => e.Name != W.sdtContent),
- new XElement(W.sdtContent, elementsInRange));
- XElement newContentControlOrdered2 = new XElement(newContentControl2.Name,
- newContentControl2.Attributes(),
- newContentControl2.Elements().OrderBy(e =>
- {
- if (Order_sdt.ContainsKey(e.Name))
- return Order_sdt[e.Name];
- return 999;
- }));
- commonAncestor.Add(
- elementsBeforeRange,
- newContentControlOrdered2,
- elementsAfterRange);
- }
- return newDocument;
- }
- private static Dictionary<XName, int> Order_sdt = new Dictionary<XName, int>
- {
- { W.sdtPr, 10 },
- { W.sdtEndPr, 20 },
- { W.sdtContent, 30 },
- { W.bookmarkStart, 40 },
- { W.bookmarkEnd, 50 },
- };
- private static XElement AcceptDeletedAndMoveFromParagraphMarks(XElement element)
- {
- AnnotateRunElementsWithId(element);
- AnnotateContentControlsWithRunIds(element);
- XElement newElement = (XElement)AcceptDeletedAndMoveFromParagraphMarksTransform(element);
- XElement withBlockLevelContentControls = AddBlockLevelContentControls(newElement, element);
- return withBlockLevelContentControls;
- }
- enum GroupingType
- {
- DeletedRange,
- Other,
- };
- class GroupingInfo
- {
- public GroupingType GroupingType;
- public int GroupingKey;
- };
- private static object AcceptDeletedAndMoveFromParagraphMarksTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (W.BlockLevelContentContainers.Contains(element.Name))
- {
- XElement bodySectPr = null;
- if (element.Name == W.body)
- bodySectPr = element.Element(W.sectPr);
- int currentKey = 0;
- var deletedParagraphGroupingInfo = new List<GroupingInfo>();
- int state = 0; // 0 = in non deleted paragraphs
- // 1 = in deleted paragraph
- // 2 - paragraph following deleted paragraphs
- foreach (var c in IterateBlockContentElements(element))
- {
- if (c.ThisBlockContentElement.Name == W.p)
- {
- bool paragraphMarkIsDeletedOrMovedFrom = c
- .ThisBlockContentElement
- .Elements(W.pPr)
- .Elements(W.rPr)
- .Elements()
- .Where(e => e.Name == W.del || e.Name == W.moveFrom)
- .Any();
- if (paragraphMarkIsDeletedOrMovedFrom)
- {
- if (state == 0)
- {
- state = 1;
- currentKey += 1;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo() {
- GroupingType = GroupingType.DeletedRange,
- GroupingKey = currentKey,
- });
- continue;
- }
- else if (state == 1)
- {
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.DeletedRange,
- GroupingKey = currentKey,
- });
- continue;
- }
- else if (state == 2)
- {
- state = 1;
- currentKey += 1;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.DeletedRange,
- GroupingKey = currentKey,
- });
- continue;
- }
- }
- if (state == 0)
- {
- currentKey += 1;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.Other,
- GroupingKey = currentKey,
- });
- continue;
- }
- else if (state == 1)
- {
- state = 2;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.DeletedRange,
- GroupingKey = currentKey,
- });
- continue;
- }
- else if (state == 2)
- {
- state = 0;
- currentKey += 1;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.Other,
- GroupingKey = currentKey,
- });
- continue;
- }
- }
- else if (c.ThisBlockContentElement.Name == W.tbl || c.ThisBlockContentElement.Name.Namespace == M.m)
- {
- currentKey += 1;
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.Other,
- GroupingKey = currentKey,
- });
- state = 0;
- continue;
- }
- else
- {
- // otherwise keep the same state, put in the same group, and continue
- deletedParagraphGroupingInfo.Add(
- new GroupingInfo()
- {
- GroupingType = GroupingType.Other,
- GroupingKey = currentKey,
- });
- continue;
- }
- }
- var zipped = IterateBlockContentElements(element).Zip(deletedParagraphGroupingInfo, (blc, gi) => new
- {
- BlockLevelContent = blc,
- GroupingInfo = gi,
- });
- var groupedParagraphs = zipped
- .GroupAdjacent(z => z.GroupingInfo.GroupingKey);
- // Create a new block level content container.
- XElement newBlockLevelContentContainer = new XElement(element.Name,
- element.Attributes(),
- element.Elements().Where(e => e.Name == W.tcPr),
- groupedParagraphs.Select((g, i) =>
- {
- if (g.First().GroupingInfo.GroupingType == GroupingType.DeletedRange)
- {
- XElement newParagraph = new XElement(W.p,
- #if false
- // previously, this was set to g.First()
- // 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.
- // 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
- // in the sequence of coalesced paragraph. It is possible that we should take Last when accepting revisions, but First when rejecting revisions.
- g.First().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr),
- #endif
- g.Last().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr),
- g.Select(z => CollapseParagraphTransform(z.BlockLevelContent.ThisBlockContentElement)));
- // if this contains the last paragraph in the document, and if there is no content,
- // and if the paragraph mark is deleted, then nuke the paragraph.
- var allIsDeleted = AllParaContentIsDeleted(newParagraph);
- if (allIsDeleted &&
- g.Last().BlockLevelContent.ThisBlockContentElement.Elements(W.pPr).Elements(W.rPr).Elements(W.del).Any() &&
- (g.Last().BlockLevelContent.NextBlockContentElement == null ||
- g.Last().BlockLevelContent.NextBlockContentElement.Name == W.tbl))
- return null;
- return (object)newParagraph;
- }
- else
- {
- return g.Select(z =>
- {
- var newEle = new XElement(z.BlockLevelContent.ThisBlockContentElement.Name,
- z.BlockLevelContent.ThisBlockContentElement.Attributes(),
- z.BlockLevelContent.ThisBlockContentElement.Nodes().Select(n => AcceptDeletedAndMoveFromParagraphMarksTransform(n)));
- return newEle;
- });
- }
- }),
- bodySectPr);
- return newBlockLevelContentContainer;
- }
- // Otherwise, identity clone.
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptDeletedAndMoveFromParagraphMarksTransform(n)));
- }
- return node;
- }
- // Determine if the paragraph contains any content that is not deleted.
- private static bool AllParaContentIsDeleted(XElement p)
- {
- // needs collapse
- // dir, bdo, sdt, ins, moveTo, smartTag
- var testP = (XElement)CollapseTransform(p);
- var childElements = testP.Elements();
- var contentElements = childElements
- .Where(ce =>
- {
- var b = IsRunContent(ce.Name);
- if (b != null)
- return (bool)b;
- throw new Exception("Internal error 20, found element " + ce.Name.ToString());
- });
- if (contentElements.Any())
- return false;
- return true;
- }
- // dir, bdo, sdt, ins, moveTo, smartTag
- private static object CollapseTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.dir ||
- element.Name == W.bdr ||
- element.Name == W.ins ||
- element.Name == W.moveTo ||
- element.Name == W.smartTag)
- return element.Elements();
- if (element.Name == W.sdt)
- return element.Elements(W.sdtContent).Elements();
- if (element.Name == W.pPr)
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CollapseTransform(n)));
- }
- return node;
- }
- private static bool? IsRunContent(XName ceName)
- {
- // is content
- // r, fldSimple, hyperlink, oMath, oMathPara, subDoc
- if (ceName == W.r ||
- ceName == W.fldSimple ||
- ceName == W.hyperlink ||
- ceName == W.subDoc ||
- ceName == W.smartTag ||
- ceName == W.smartTagPr ||
- ceName.Namespace == M.m)
- return true;
- // not content
- // bookmarkStart, bookmarkEnd, commentRangeStart, commentRangeEnd, del, moveFrom, proofErr
- if (ceName == W.bookmarkStart ||
- ceName == W.bookmarkEnd ||
- ceName == W.commentRangeStart ||
- ceName == W.commentRangeEnd ||
- ceName == W.customXmlDelRangeStart ||
- ceName == W.customXmlDelRangeEnd ||
- ceName == W.customXmlInsRangeStart ||
- ceName == W.customXmlInsRangeEnd ||
- ceName == W.customXmlMoveFromRangeStart ||
- ceName == W.customXmlMoveFromRangeEnd ||
- ceName == W.customXmlMoveToRangeStart ||
- ceName == W.customXmlMoveToRangeEnd ||
- ceName == W.del ||
- ceName == W.moveFrom ||
- ceName == W.moveFromRangeStart ||
- ceName == W.moveFromRangeEnd ||
- ceName == W.moveToRangeStart ||
- ceName == W.moveToRangeEnd ||
- ceName == W.permStart ||
- ceName == W.permEnd ||
- ceName == W.proofErr)
- return false;
- return null;
- }
- private static IEnumerable<Tag> DescendantAndSelfTags(XElement element)
- {
- yield return new Tag
- {
- Element = element,
- TagType = TagTypeEnum.Element
- };
- Stack<IEnumerator<XElement>> iteratorStack = new Stack<IEnumerator<XElement>>();
- iteratorStack.Push(element.Elements().GetEnumerator());
- while (iteratorStack.Count > 0)
- {
- if (iteratorStack.Peek().MoveNext())
- {
- XElement currentXElement = iteratorStack.Peek().Current;
- if (!currentXElement.Nodes().Any())
- {
- yield return new Tag()
- {
- Element = currentXElement,
- TagType = TagTypeEnum.EmptyElement
- };
- continue;
- }
- yield return new Tag()
- {
- Element = currentXElement,
- TagType = TagTypeEnum.Element
- };
- iteratorStack.Push(currentXElement.Elements().GetEnumerator());
- continue;
- }
- iteratorStack.Pop();
- if (iteratorStack.Count > 0)
- yield return new Tag()
- {
- Element = iteratorStack.Peek().Current,
- TagType = TagTypeEnum.EndElement
- };
- }
- yield return new Tag
- {
- Element = element,
- TagType = TagTypeEnum.EndElement
- };
- }
- private class PotentialInRangeElements
- {
- public List<XElement> PotentialStartElementTagsInRange;
- public List<XElement> PotentialEndElementTagsInRange;
- public PotentialInRangeElements()
- {
- PotentialStartElementTagsInRange = new List<XElement>();
- PotentialEndElementTagsInRange = new List<XElement>();
- }
- }
- private enum TagTypeEnum
- {
- Element,
- EndElement,
- EmptyElement
- }
- private class Tag
- {
- public XElement Element;
- public TagTypeEnum TagType;
- }
- private static object AcceptDeletedAndMovedFromContentControlsTransform(XNode node,
- XElement[] contentControlElementsToCollapse,
- XElement[] moveFromElementsToDelete)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.sdt && contentControlElementsToCollapse.Contains(element))
- return element
- .Element(W.sdtContent)
- .Nodes()
- .Select(n => AcceptDeletedAndMovedFromContentControlsTransform(
- n, contentControlElementsToCollapse, moveFromElementsToDelete));
- if (moveFromElementsToDelete.Contains(element))
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptDeletedAndMovedFromContentControlsTransform(
- n, contentControlElementsToCollapse, moveFromElementsToDelete)));
- }
- return node;
- }
- private static XElement AcceptDeletedAndMovedFromContentControls(XElement documentRootElement)
- {
- string wordProcessingNamespacePrefix = documentRootElement.GetPrefixOfNamespace(W.w);
- // The following lists contain the elements that are between start/end elements.
- List<XElement> startElementTagsInDeleteRange = new List<XElement>();
- List<XElement> endElementTagsInDeleteRange = new List<XElement>();
- List<XElement> startElementTagsInMoveFromRange = new List<XElement>();
- List<XElement> endElementTagsInMoveFromRange = new List<XElement>();
- // Following are the elements that *may* be in a range that has both start and end
- // elements.
- Dictionary<string, PotentialInRangeElements> potentialDeletedElements =
- new Dictionary<string, PotentialInRangeElements>();
- Dictionary<string, PotentialInRangeElements> potentialMoveFromElements =
- new Dictionary<string, PotentialInRangeElements>();
- foreach (var tag in DescendantAndSelfTags(documentRootElement))
- {
- if (tag.Element.Name == W.customXmlDelRangeStart)
- {
- string id = tag.Element.Attribute(W.id).Value;
- potentialDeletedElements.Add(id, new PotentialInRangeElements());
- continue;
- }
- if (tag.Element.Name == W.customXmlDelRangeEnd)
- {
- string id = tag.Element.Attribute(W.id).Value;
- if (potentialDeletedElements.ContainsKey(id))
- {
- startElementTagsInDeleteRange.AddRange(
- potentialDeletedElements[id].PotentialStartElementTagsInRange);
- endElementTagsInDeleteRange.AddRange(
- potentialDeletedElements[id].PotentialEndElementTagsInRange);
- potentialDeletedElements.Remove(id);
- }
- continue;
- }
- if (tag.Element.Name == W.customXmlMoveFromRangeStart)
- {
- string id = tag.Element.Attribute(W.id).Value;
- potentialMoveFromElements.Add(id, new PotentialInRangeElements());
- continue;
- }
- if (tag.Element.Name == W.customXmlMoveFromRangeEnd)
- {
- string id = tag.Element.Attribute(W.id).Value;
- if (potentialMoveFromElements.ContainsKey(id))
- {
- startElementTagsInMoveFromRange.AddRange(
- potentialMoveFromElements[id].PotentialStartElementTagsInRange);
- endElementTagsInMoveFromRange.AddRange(
- potentialMoveFromElements[id].PotentialEndElementTagsInRange);
- potentialMoveFromElements.Remove(id);
- }
- continue;
- }
- if (tag.Element.Name == W.sdt)
- {
- if (tag.TagType == TagTypeEnum.Element)
- {
- foreach (var id in potentialDeletedElements)
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- foreach (var id in potentialMoveFromElements)
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- continue;
- }
- if (tag.TagType == TagTypeEnum.EmptyElement)
- {
- foreach (var id in potentialDeletedElements)
- {
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- }
- foreach (var id in potentialMoveFromElements)
- {
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- }
- continue;
- }
- if (tag.TagType == TagTypeEnum.EndElement)
- {
- foreach (var id in potentialDeletedElements)
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- foreach (var id in potentialMoveFromElements)
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- continue;
- }
- throw new PowerToolsInvalidDataException("Should not have reached this point.");
- }
- if (potentialMoveFromElements.Count() > 0 &&
- tag.Element.Name != W.moveFromRangeStart &&
- tag.Element.Name != W.moveFromRangeEnd &&
- tag.Element.Name != W.customXmlMoveFromRangeStart &&
- tag.Element.Name != W.customXmlMoveFromRangeEnd)
- {
- if (tag.TagType == TagTypeEnum.Element)
- {
- foreach (var id in potentialMoveFromElements)
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- continue;
- }
- if (tag.TagType == TagTypeEnum.EmptyElement)
- {
- foreach (var id in potentialMoveFromElements)
- {
- id.Value.PotentialStartElementTagsInRange.Add(tag.Element);
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- }
- continue;
- }
- if (tag.TagType == TagTypeEnum.EndElement)
- {
- foreach (var id in potentialMoveFromElements)
- id.Value.PotentialEndElementTagsInRange.Add(tag.Element);
- continue;
- }
- }
- }
- var contentControlElementsToCollapse = startElementTagsInDeleteRange
- .Intersect(endElementTagsInDeleteRange)
- .ToArray();
- var elementsToDeleteBecauseMovedFrom = startElementTagsInMoveFromRange
- .Intersect(endElementTagsInMoveFromRange)
- .ToArray();
- if (contentControlElementsToCollapse.Length > 0 ||
- elementsToDeleteBecauseMovedFrom.Length > 0)
- {
- var newDoc = AcceptDeletedAndMovedFromContentControlsTransform(documentRootElement,
- contentControlElementsToCollapse, elementsToDeleteBecauseMovedFrom);
- return newDoc as XElement;
- }
- else
- return documentRootElement;
- }
- private static object AcceptMoveFromRangesTransform(XNode node,
- XElement[] elementsToDelete)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (elementsToDelete.Contains(element))
- return null;
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n =>
- AcceptMoveFromRangesTransform(n, elementsToDelete)));
- }
- return node;
- }
- private static object CoalesqueParagraphEndTagsInMoveFromTransform(XNode node,
- IGrouping<MoveFromCollectionType, XElement> g)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.p)
- return new XElement(W.p,
- element.Attributes(),
- element.Elements(),
- g.Skip(1).Select(p => CollapseParagraphTransform(p)));
- else
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n =>
- CoalesqueParagraphEndTagsInMoveFromTransform(n, g)));
- }
- return node;
- }
- private enum DeletedCellCollectionType
- {
- DeletedCell,
- Other
- };
- // For each table row, group deleted cells plus the cell before any deleted cell.
- // Produce a new cell that has gridSpan set appropriately for group, and clone everything
- // else.
- private static object AcceptDeletedCellsTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.tr)
- {
- var groupedCells = element
- .Elements()
- .GroupAdjacent(e =>
- {
- XElement cellAfter = e.ElementsAfterSelf(W.tc).FirstOrDefault();
- bool cellAfterIsDeleted = cellAfter != null &&
- cellAfter.Descendants(W.cellDel).Any();
- if (e.Name == W.tc &&
- (cellAfterIsDeleted || e.Descendants(W.cellDel).Any()))
- {
- var a = new
- {
- CollectionType = DeletedCellCollectionType.DeletedCell,
- Disambiguator = new[] { e }
- .Concat(e.SiblingsBeforeSelfReverseDocumentOrder())
- .Where(z => z.Name == W.tc &&
- !z.Descendants(W.cellDel).Any())
- .FirstOrDefault()
- };
- return a;
- }
- var a2 = new
- {
- CollectionType = DeletedCellCollectionType.Other,
- Disambiguator = e
- };
- return a2;
- });
- var tr = new XElement(W.tr,
- element.Attributes(),
- groupedCells.Select(g =>
- {
- if (g.Key.CollectionType == DeletedCellCollectionType.DeletedCell
- && g.First().Descendants(W.cellDel).Any())
- return null;
- if (g.Key.CollectionType == DeletedCellCollectionType.Other)
- return (object)g;
- XElement gridSpanElement = g
- .First()
- .Elements(W.tcPr)
- .Elements(W.gridSpan)
- .FirstOrDefault();
- int gridSpan = gridSpanElement != null ?
- (int)gridSpanElement.Attribute(W.val) :
- 1;
- int newGridSpan = gridSpan + g.Count() - 1;
- XElement currentTcPr = g.First().Elements(W.tcPr).FirstOrDefault();
- XElement newTcPr = new XElement(W.tcPr,
- currentTcPr != null ? currentTcPr.Attributes() : null,
- new XElement(W.gridSpan,
- new XAttribute(W.val, newGridSpan)),
- currentTcPr.Elements().Where(e => e.Name != W.gridSpan));
- var orderedTcPr = new XElement(W.tcPr,
- newTcPr.Elements().OrderBy(e =>
- {
- if (Order_tcPr.ContainsKey(e.Name))
- return Order_tcPr[e.Name];
- return 999;
- }));
- XElement newTc = new XElement(W.tc,
- orderedTcPr,
- g.First().Elements().Where(e => e.Name != W.tcPr));
- return (object)newTc;
- }));
- return tr;
- }
- // Identity clone
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => AcceptDeletedCellsTransform(n)));
- }
- return node;
- }
- #if false
- <w:tr>
- <w:tc>
- <w:tcPr>
- <w:tcW w:w="5016"
- w:type="dxa" />
- </w:tcPr>
- </w:tc>
- </w:tr>
- #endif
- private static XName[] BlockLevelElements = new[] {
- W.p,
- W.tbl,
- W.sdt,
- W.del,
- W.ins,
- M.oMath,
- M.oMathPara,
- W.moveTo,
- };
- private static object RemoveRowsLeftEmptyByMoveFrom(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- if (element.Name == W.tr)
- {
- var nonEmptyCells = element.Elements(W.tc).Any(tc => tc.Elements().Any(tcc => BlockLevelElements.Contains(tcc.Name)));
- if (nonEmptyCells)
- {
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => RemoveRowsLeftEmptyByMoveFrom(n)));
- }
- return null;
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => RemoveRowsLeftEmptyByMoveFrom(n)));
- }
- return node;
- }
- public static XName[] TrackedRevisionsElements = new[]
- {
- W.cellDel,
- W.cellIns,
- W.cellMerge,
- W.customXmlDelRangeEnd,
- W.customXmlDelRangeStart,
- W.customXmlInsRangeEnd,
- W.customXmlInsRangeStart,
- W.del,
- W.delInstrText,
- W.delText,
- W.ins,
- W.moveFrom,
- W.moveFromRangeEnd,
- W.moveFromRangeStart,
- W.moveTo,
- W.moveToRangeEnd,
- W.moveToRangeStart,
- W.numberingChange,
- W.pPrChange,
- W.rPrChange,
- W.sectPrChange,
- W.tblGridChange,
- W.tblPrChange,
- W.tblPrExChange,
- W.tcPrChange,
- W.trPrChange,
- };
- public static bool PartHasTrackedRevisions(OpenXmlPart part)
- {
- return part.GetXDocument()
- .Descendants()
- .Any(e => TrackedRevisionsElements.Contains(e.Name));
- }
- public static bool HasTrackedRevisions(WmlDocument document)
- {
- using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
- {
- using (WordprocessingDocument wdoc = streamDoc.GetWordprocessingDocument())
- {
- return RevisionAccepter.HasTrackedRevisions(wdoc);
- }
- }
- }
- public static bool HasTrackedRevisions(WordprocessingDocument doc)
- {
- if (PartHasTrackedRevisions(doc.MainDocumentPart))
- return true;
- foreach (var part in doc.MainDocumentPart.HeaderParts)
- if (PartHasTrackedRevisions(part))
- return true;
- foreach (var part in doc.MainDocumentPart.FooterParts)
- if (PartHasTrackedRevisions(part))
- return true;
- if (doc.MainDocumentPart.EndnotesPart != null)
- if (PartHasTrackedRevisions(doc.MainDocumentPart.EndnotesPart))
- return true;
- if (doc.MainDocumentPart.FootnotesPart != null)
- if (PartHasTrackedRevisions(doc.MainDocumentPart.FootnotesPart))
- return true;
- return false;
- }
- }
- public partial class WmlDocument : OpenXmlPowerToolsDocument
- {
- public WmlDocument AcceptRevisions(WmlDocument document)
- {
- return RevisionAccepter.AcceptRevisions(document);
- }
- public bool HasTrackedRevisions(WmlDocument document)
- {
- return RevisionAccepter.HasTrackedRevisions(document);
- }
- }
- public class BlockContentInfo
- {
- public XElement PreviousBlockContentElement;
- public XElement ThisBlockContentElement;
- public XElement NextBlockContentElement;
- }
- public static class RevisionAccepterExtensions
- {
- private static void InitializeParagraphInfo(XElement contentContext)
- {
- if (!(W.BlockLevelContentContainers.Contains(contentContext.Name)))
- throw new ArgumentException(
- "GetParagraphInfo called for element that is not child of content container");
- XElement prev = null;
- foreach (var content in contentContext.Elements())
- {
- // This may return null, indicating that there is no descendant paragraph. For
- // example, comment elements have no descendant elements.
- XElement paragraph = content
- .DescendantsAndSelf()
- .Where(e => e.Name == W.p || e.Name == W.tc || e.Name == W.txbxContent)
- .FirstOrDefault();
- if (paragraph != null &&
- (paragraph.Name == W.tc || paragraph.Name == W.txbxContent))
- paragraph = null;
- BlockContentInfo pi = new BlockContentInfo()
- {
- PreviousBlockContentElement = prev,
- ThisBlockContentElement = paragraph
- };
- content.AddAnnotation(pi);
- prev = content;
- }
- }
- public static BlockContentInfo GetParagraphInfo(this XElement contentElement)
- {
- BlockContentInfo paragraphInfo = contentElement.Annotation<BlockContentInfo>();
- if (paragraphInfo != null)
- return paragraphInfo;
- InitializeParagraphInfo(contentElement.Parent);
- return contentElement.Annotation<BlockContentInfo>();
- }
- public static IEnumerable<XElement> ContentElementsBeforeSelf(this XElement element)
- {
- XElement current = element;
- while (true)
- {
- BlockContentInfo pi = current.GetParagraphInfo();
- if (pi.PreviousBlockContentElement == null)
- yield break;
- yield return pi.PreviousBlockContentElement;
- current = pi.PreviousBlockContentElement;
- }
- }
- }
- }
- /// Markup that this code processes:
- ///
- /// delText
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: MovedText.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to w:t element
- ///
- /// del (deleted run content)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements and descendant elements.
- /// Reject:
- /// Transform to w:ins element
- /// Then Accept
- ///
- /// ins (inserted run content)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: InsertedParagraphsAndRuns.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Collapse these elements.
- /// Reject:
- /// Transform to w:del element, and child w:t transform to w:delText element
- /// Then Accept
- ///
- /// ins (inserted paragraph)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: InsertedParagraphsAndRuns.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to w:del element
- /// Then Accept
- ///
- /// del (deleted paragraph mark)
- /// Method: AcceptDeletedAndMoveFromParagraphMarksTransform
- /// Sample document: VariousTableRevisions.docx (deleted paragraph mark in paragraph in
- /// content control)
- /// Reviewed: tristan and zeyad ****************************************
- /// Semantics:
- /// Find all adjacent paragraps that have this element.
- /// Group adjacent paragraphs plus the paragraph following paragraph that has this element.
- /// Replace grouped paragraphs with a new paragraph containing the content from all grouped
- /// paragraphs. Use the paragraph properties from the first paragraph in the group.
- /// Reject:
- /// Transform to w:ins element
- /// Then Accept
- ///
- /// del (deleted table row)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Match w:tr/w:trPr/w:del, remove w:tr.
- /// Reject:
- /// Transform to w:ins
- /// Then Accept
- ///
- /// ins (inserted table row)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to w:del
- /// Then Accept
- ///
- /// del (deleted math control character)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: DeletedMathControlCharacter.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Match m:f/m:fPr/m:ctrlPr/w:del, remove m:f.
- /// Reject:
- /// Transform to w:ins
- /// Then Accept
- ///
- /// ins (inserted math control character)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: InsertedMathControlCharacter.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to w:del
- /// Then Accept
- ///
- /// moveTo (move destination paragraph mark)
- /// Method: AcceptMoveFromMoveToTransform
- /// Sample document: MovedText.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to moveFrom
- /// Then Accept
- ///
- /// moveTo (move destination run content)
- /// Method: AcceptMoveFromMoveToTransform
- /// Sample document: MovedText.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Collapse these elements.
- /// Reject:
- /// Transform to moveFrom
- /// Then Accept
- ///
- /// moveFrom (move source paragraph mark)
- /// Methods: AcceptDeletedAndMoveFromParagraphMarksTransform, AcceptParagraphEndTagsInMoveFromTransform
- /// Sample document: MovedText.docx
- /// Reviewed: tristan and zeyad ****************************************
- /// Semantics:
- /// Find all adjacent paragraps that have this element or deleted paragraph mark.
- /// Group adjacent paragraphs plus the paragraph following paragraph that has this element.
- /// Replace grouped paragraphs with a new paragraph containing the content from all grouped
- /// paragraphs.
- /// This is handled in the same code that handles del (deleted paragraph mark).
- /// Reject:
- /// Transform to moveTo
- /// Then Accept
- ///
- /// moveFrom (move source run content)
- /// Method: AcceptMoveFromMoveToTransform
- /// Sample document: MovedText.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to moveTo
- /// Then Accept
- ///
- /// moveFromRangeStart
- /// moveFromRangeEnd
- /// Method: AcceptMoveFromRanges
- /// Sample document: MovedText.docx
- /// Semantics:
- /// Find pairs of elements. Remove all elements that have both start and end tags in a
- /// range.
- /// Reject:
- /// Transform to moveToRangeStart, moveToRangeEnd
- /// Then Accept
- ///
- /// moveToRangeStart
- /// moveToRangeEnd
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: MovedText.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to moveFromRangeStart, moveFromRangeEnd
- /// Then Accept
- ///
- /// customXmlDelRangeStart
- /// customXmlDelRangeEnd
- /// customXmlMoveFromRangeStart
- /// customXmlMoveFromRangeEnd
- /// Method: AcceptDeletedAndMovedFromContentControls
- /// Reviewed: tristan and zeyad ****************************************
- /// Semantics:
- /// Find pairs of start/end elements, matching id attributes. Collapse sdt
- /// elements that have both start and end tags in a range.
- /// Reject:
- /// Transform to customXmlInsRangeStart, customXmlInsRangeEnd, customXmlMoveToRangeStart, customXmlMoveToRangeEnd
- /// Then Accept
- ///
- /// customXmlInsRangeStart
- /// customXmlInsRangeEnd
- /// customXmlMoveToRangeStart
- /// customXmlMoveToRangeEnd
- /// Method: AcceptAllOtherRevisionsTransform
- /// Reviewed: tristan and zeyad ****************************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to customXmlDelRangeStart, customXmlDelRangeEnd, customXmlMoveFromRangeStart, customXmlMoveFromRangeEnd
- /// Then Accept
- ///
- /// delInstrText (deleted field code)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: NumberingParagraphPropertiesChange.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Transform to instrText
- /// Then Accept
- /// 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
- ///
- /// ins (inserted numbering properties)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: InsertedNumberingProperties.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject
- /// Remove the containing w:numPr
- ///
- /// pPrChange (revision information for paragraph properties)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: ParagraphAndRunPropertyRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace pPr with the pPr in pPrChange
- ///
- /// rPrChange (revision information for run properties)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: ParagraphAndRunPropertyRevisions.docx
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace rPr with the rPr in rPrChange
- ///
- /// rPrChange (revision information for run properties on the paragraph mark)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: ParagraphAndRunPropertyRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace rPr with the rPr in rPrChange.
- ///
- /// numberingChange (previous numbering field properties)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: NumberingFieldPropertiesChange.docx
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Remove these elements.
- /// These are there for numbering created via fields, and are not important.
- ///
- /// numberingChange (previous paragraph numbering properties)
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: NumberingFieldPropertiesChange.docx
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Remove these elements.
- ///
- /// sectPrChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: SectionPropertiesChange.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace sectPr with the sectPr in sectPrChange
- ///
- /// tblGridChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: TableGridChange.docx
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace tblGrid with the tblGrid in tblGridChange
- ///
- /// tblPrChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: TableGridChange.docx
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace tblPr with the tblPr in tblPrChange
- ///
- /// tblPrExChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace tblPrEx with the tblPrEx in tblPrExChange
- ///
- /// tcPrChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: TableGridChange.docx
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace tcPr with the tcPr in tcPrChange
- ///
- /// trPrChange
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: VariousTableRevisions.docx
- /// Reviewed: zeyad ***************************
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// Replace trPr with the trPr in trPrChange
- ///
- /// celDel
- /// Method: AcceptDeletedCellsTransform
- /// Sample document: HorizontallyMergedCells.docx
- /// Semantics:
- /// Group consecutive deleted cells, and remove them.
- /// Adjust the cell before deleted cells:
- /// Increase gridSpan by the number of deleted cells that are removed.
- /// Reject:
- /// Remove this element
- ///
- /// celIns
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: HorizontallyMergedCells11.docx
- /// Semantics:
- /// Remove these elements.
- /// Reject:
- /// If a w:tc contains w:tcPr/w:cellIns, then remove the cell
- ///
- /// cellMerge
- /// Method: AcceptAllOtherRevisionsTransform
- /// Sample document: MergedCell.docx
- /// Semantics:
- /// Transform cellMerge with a parent of tcPr, with attribute w:vMerge="rest"
- /// to <w:vMerge w:val="restart"/>.
- /// Transform cellMerge with a parent of tcPr, with attribute w:vMerge="cont"
- /// to <w:vMerge w:val="continue"/>
- ///
- /// The following items need to be addressed in a future release:
- /// - inserted run inside deleted paragraph - moveTo is same as insert
- /// - must increase w:val attribute of the w:gridSpan element of the
- /// cell immediately preceding the group of deleted cells by the
- /// ***sum*** of the values of the w:val attributes of w:gridSpan
- /// elements of each of the deleted cells.
|