FormattingAssembler.cs 153 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755
  1. // Copyright (c) Microsoft. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Xml.Linq;
  8. using DocumentFormat.OpenXml.Packaging;
  9. using System.Drawing;
  10. namespace OpenXmlPowerTools
  11. {
  12. public class FormattingAssemblerSettings
  13. {
  14. public bool RemoveStyleNamesFromParagraphAndRunProperties;
  15. public bool ClearStyles;
  16. public bool OrderElementsPerStandard;
  17. public bool CreateHtmlConverterAnnotationAttributes;
  18. public bool RestrictToSupportedNumberingFormats;
  19. public bool RestrictToSupportedLanguages;
  20. public ListItemRetrieverSettings ListItemRetrieverSettings;
  21. public FormattingAssemblerSettings()
  22. {
  23. RemoveStyleNamesFromParagraphAndRunProperties = true;
  24. ClearStyles = true;
  25. OrderElementsPerStandard = true;
  26. CreateHtmlConverterAnnotationAttributes = true;
  27. RestrictToSupportedNumberingFormats = false;
  28. RestrictToSupportedLanguages = false;
  29. ListItemRetrieverSettings = new ListItemRetrieverSettings();
  30. }
  31. }
  32. public static class FormattingAssembler
  33. {
  34. public static WmlDocument AssembleFormatting(WmlDocument document, FormattingAssemblerSettings settings)
  35. {
  36. using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
  37. {
  38. using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
  39. {
  40. AssembleFormatting(doc, settings);
  41. }
  42. return streamDoc.GetModifiedWmlDocument();
  43. }
  44. }
  45. public static void AssembleFormatting(WordprocessingDocument wDoc, FormattingAssemblerSettings settings)
  46. {
  47. FormattingAssemblerInfo fai = new FormattingAssemblerInfo();
  48. XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  49. XElement defaultParagraphStyle = sXDoc
  50. .Root
  51. .Elements(W.style)
  52. .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true &&
  53. (string)st.Attribute(W.type) == "paragraph");
  54. if (defaultParagraphStyle != null)
  55. fai.DefaultParagraphStyleName = (string)defaultParagraphStyle.Attribute(W.styleId);
  56. XElement defaultCharacterStyle = sXDoc
  57. .Root
  58. .Elements(W.style)
  59. .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true &&
  60. (string)st.Attribute(W.type) == "character");
  61. if (defaultCharacterStyle != null)
  62. fai.DefaultCharacterStyleName = (string)defaultCharacterStyle.Attribute(W.styleId);
  63. XElement defaultTableStyle = sXDoc
  64. .Root
  65. .Elements(W.style)
  66. .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true &&
  67. (string)st.Attribute(W.type) == "table");
  68. if (defaultTableStyle != null)
  69. fai.DefaultTableStyleName = (string)defaultTableStyle.Attribute(W.styleId);
  70. ListItemRetrieverSettings listItemRetrieverSettings = new ListItemRetrieverSettings();
  71. AssembleListItemInformation(wDoc, settings.ListItemRetrieverSettings);
  72. foreach (var part in wDoc.ContentParts())
  73. {
  74. var pxd = part.GetXDocument();
  75. FixNonconformantHexValues(pxd.Root);
  76. AnnotateWithGlobalDefaults(wDoc, pxd.Root, settings);
  77. AnnotateTablesWithTableStyles(wDoc, pxd.Root);
  78. AnnotateParagraphs(fai, wDoc, pxd.Root, settings);
  79. AnnotateRuns(fai, wDoc, pxd.Root, settings);
  80. }
  81. NormalizeListItems(fai, wDoc, settings);
  82. if (settings.ClearStyles)
  83. ClearStyles(wDoc);
  84. foreach (var part in wDoc.ContentParts())
  85. {
  86. var pxd = part.GetXDocument();
  87. pxd.Root.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
  88. FormattingAssembler.NormalizePropsForPart(pxd, settings);
  89. var newRoot = (XElement)CleanupTransform(pxd.Root);
  90. pxd.Root.ReplaceWith(newRoot);
  91. part.PutXDocument();
  92. }
  93. }
  94. private static void FixNonconformantHexValues(XElement root)
  95. {
  96. foreach (var tblLook in root.Descendants(W.tblLook))
  97. {
  98. if (tblLook.Attributes().Any(a => a.Name != W.val))
  99. continue;
  100. if (tblLook.Attribute(W.val) == null)
  101. continue;
  102. string hexValue = tblLook.Attribute(W.val).Value;
  103. int val = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
  104. tblLook.Add(new XAttribute(W.firstRow, (val & 0x0020) != 0 ? "1" : "0"));
  105. tblLook.Add(new XAttribute(W.lastRow, (val & 0x0040) != 0 ? "1" : "0"));
  106. tblLook.Add(new XAttribute(W.firstColumn, (val & 0x0080) != 0 ? "1" : "0"));
  107. tblLook.Add(new XAttribute(W.lastColumn, (val & 0x0100) != 0 ? "1" : "0"));
  108. tblLook.Add(new XAttribute(W.noHBand, (val & 0x0200) != 0 ? "1" : "0"));
  109. tblLook.Add(new XAttribute(W.noVBand, (val & 0x0400) != 0 ? "1" : "0"));
  110. }
  111. foreach (var cnfStyle in root.Descendants(W.cnfStyle))
  112. {
  113. if (cnfStyle.Attributes().Any(a => a.Name != W.val))
  114. continue;
  115. if (cnfStyle.Attribute(W.val) == null)
  116. continue;
  117. var va = cnfStyle.Attribute(W.val).Value.ToArray();
  118. cnfStyle.Add(new XAttribute(W.firstRow, va[0]));
  119. cnfStyle.Add(new XAttribute(W.lastRow, va[1]));
  120. cnfStyle.Add(new XAttribute(W.firstColumn, va[2]));
  121. cnfStyle.Add(new XAttribute(W.lastColumn, va[3]));
  122. cnfStyle.Add(new XAttribute(W.oddVBand, va[4]));
  123. cnfStyle.Add(new XAttribute(W.evenVBand, va[5]));
  124. cnfStyle.Add(new XAttribute(W.oddHBand, va[6]));
  125. cnfStyle.Add(new XAttribute(W.evenHBand, va[7]));
  126. cnfStyle.Add(new XAttribute(W.firstRowLastColumn, va[8]));
  127. cnfStyle.Add(new XAttribute(W.firstRowFirstColumn, va[9]));
  128. cnfStyle.Add(new XAttribute(W.lastRowLastColumn, va[10]));
  129. cnfStyle.Add(new XAttribute(W.lastRowFirstColumn, va[11]));
  130. }
  131. }
  132. private static object CleanupTransform(XNode node)
  133. {
  134. XElement element = node as XElement;
  135. if (element != null)
  136. {
  137. if (element.Name == W.tabs && element.Element(W.tab) == null)
  138. return null;
  139. if (element.Name == W.tblStyleRowBandSize || element.Name == W.tblStyleColBandSize)
  140. return null;
  141. // a cleaner solution would be to not include the w:ins and w:del elements when rolling up the paragraph run properties into
  142. // the run properties.
  143. if ((element.Name == W.ins || element.Name == W.del) && element.Parent.Name == W.rPr)
  144. {
  145. if (element.Parent.Parent.Name == W.r || element.Parent.Parent.Name == W.rPrChange)
  146. return null;
  147. }
  148. return new XElement(element.Name,
  149. element.Attributes(),
  150. element.Nodes().Select(n => CleanupTransform(n)));
  151. }
  152. return node;
  153. }
  154. private static void ClearStyles(WordprocessingDocument wDoc)
  155. {
  156. var stylePart = wDoc.MainDocumentPart.StyleDefinitionsPart;
  157. var sXDoc = stylePart.GetXDocument();
  158. var newRoot = new XElement(sXDoc.Root.Name,
  159. sXDoc.Root.Attributes(),
  160. sXDoc.Root.Elements().Select(e =>
  161. {
  162. if (e.Name != W.style)
  163. return e;
  164. return new XElement(e.Name,
  165. e.Attributes(),
  166. e.Element(W.name),
  167. new XElement(W.pPr),
  168. new XElement(W.rPr));
  169. }));
  170. var globalrPr = newRoot
  171. .Elements(W.docDefaults)
  172. .Elements(W.rPrDefault)
  173. .Elements(W.rPr)
  174. .FirstOrDefault();
  175. if (globalrPr != null)
  176. globalrPr.ReplaceWith(new XElement(W.rPr));
  177. var globalpPr = newRoot
  178. .Elements(W.docDefaults)
  179. .Elements(W.pPrDefault)
  180. .Elements(W.pPr)
  181. .FirstOrDefault();
  182. if (globalpPr != null)
  183. globalpPr.ReplaceWith(new XElement(W.pPr));
  184. sXDoc.Root.ReplaceWith(newRoot);
  185. stylePart.PutXDocument();
  186. }
  187. private static void NormalizeListItems(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, FormattingAssemblerSettings settings)
  188. {
  189. foreach (var part in wDoc.ContentParts())
  190. {
  191. var pxd = part.GetXDocument();
  192. XElement newRoot = (XElement)NormalizeListItemsTransform(fai, wDoc, pxd.Root, settings);
  193. if (newRoot.Attribute(XNamespace.Xmlns + "pt14") == null)
  194. newRoot.Add(new XAttribute(XNamespace.Xmlns + "pt14", PtOpenXml.pt.NamespaceName));
  195. if (newRoot.Attribute(XNamespace.Xmlns + "mc") == null)
  196. newRoot.Add(new XAttribute(XNamespace.Xmlns + "mc", MC.mc.NamespaceName));
  197. pxd.Root.ReplaceWith(newRoot);
  198. }
  199. }
  200. private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XNode node, FormattingAssemblerSettings settings)
  201. {
  202. var element = node as XElement;
  203. if (element != null)
  204. {
  205. if (element.Name == W.p)
  206. {
  207. var li = ListItemRetriever.RetrieveListItem(wDoc, element, settings.ListItemRetrieverSettings);
  208. if (li != null)
  209. {
  210. ListItemRetriever.ListItemInfo listItemInfo = element.Annotation<ListItemRetriever.ListItemInfo>();
  211. var newParaProps = new XElement(W.pPr,
  212. element.Elements(W.pPr).Elements().Where(e => e.Name != W.numPr)
  213. );
  214. XElement listItemRunProps = null;
  215. List<XAttribute> listItemHtmlAttributes = new List<XAttribute>();
  216. int? abstractNumId = null;
  217. if (listItemInfo != null)
  218. {
  219. abstractNumId = listItemInfo.AbstractNumId;
  220. var paraStyleRunProps = CharStyleRollup(fai, wDoc, element);
  221. var paragraphStyleName = (string)element
  222. .Elements(W.pPr)
  223. .Elements(W.pStyle)
  224. .Attributes(W.val)
  225. .FirstOrDefault();
  226. string defaultStyleName = (string)wDoc
  227. .MainDocumentPart
  228. .StyleDefinitionsPart
  229. .GetXDocument()
  230. .Root
  231. .Elements(W.style)
  232. .Where(s => (string)s.Attribute(W.type) == "paragraph" && s.Attribute(W._default).ToBoolean() == true)
  233. .Attributes(W.styleId)
  234. .FirstOrDefault();
  235. if (paragraphStyleName == null)
  236. paragraphStyleName = defaultStyleName;
  237. XDocument stylesXDoc = wDoc
  238. .MainDocumentPart
  239. .StyleDefinitionsPart
  240. .GetXDocument();
  241. // put together run props for list item.
  242. XElement lvlStyleRpr = ParaStyleRunPropsStack(wDoc, paragraphStyleName)
  243. .Aggregate(new XElement(W.rPr),
  244. (r, s) =>
  245. {
  246. var newCharStyleRunProps = MergeStyleElement(s, r);
  247. return newCharStyleRunProps;
  248. });
  249. var mergedRunProps = MergeStyleElement(lvlStyleRpr, paraStyleRunProps);
  250. var accumulatedRunProps = element.Elements(PtOpenXml.pPr).Elements(W.rPr).FirstOrDefault();
  251. if (accumulatedRunProps != null)
  252. mergedRunProps = MergeStyleElement(accumulatedRunProps, mergedRunProps);
  253. var listItemLvl = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(element));
  254. var listItemLvlRunProps = listItemLvl.Elements(W.rPr).FirstOrDefault();
  255. listItemRunProps = MergeStyleElement(listItemLvlRunProps, mergedRunProps);
  256. string numFmt = null;
  257. string format = null;
  258. numFmt = (string)listItemLvl.Elements(W.numFmt).Attributes(W.val).FirstOrDefault();
  259. if (numFmt == null)
  260. {
  261. var mcAlternativeContent = listItemLvl.Descendants(MC.AlternateContent).FirstOrDefault();
  262. if (mcAlternativeContent != null)
  263. {
  264. var choice = mcAlternativeContent.Element(MC.Choice);
  265. if (choice != null)
  266. {
  267. numFmt = (string)choice.Elements(W.numFmt).Attributes(W.val).FirstOrDefault();
  268. format = (string)choice.Elements(W.numFmt).Attributes(W.format).FirstOrDefault();
  269. }
  270. }
  271. }
  272. if (numFmt == "bullet")
  273. {
  274. listItemRunProps.Elements(W.rtl).Remove();
  275. listItemHtmlAttributes.Add(
  276. new XAttribute(PtOpenXml.HtmlStructure, "ul")
  277. );
  278. listItemHtmlAttributes.Add(
  279. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: circle;")
  280. );
  281. }
  282. else
  283. {
  284. var pPr = element.Element(PtOpenXml.pPr);
  285. if (pPr != null)
  286. {
  287. XElement bidiel = pPr.Element(W.bidi);
  288. bool bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true);
  289. if (bidi)
  290. {
  291. listItemRunProps = MergeStyleElement(new XElement(W.rPr,
  292. new XElement(W.rtl)), listItemRunProps);
  293. }
  294. }
  295. listItemHtmlAttributes.Add(
  296. new XAttribute(PtOpenXml.HtmlStructure, "ol")
  297. );
  298. if (numFmt == "decimal")
  299. {
  300. listItemHtmlAttributes.Add(
  301. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: decimal;")
  302. );
  303. }
  304. else if (numFmt == "lowerLetter")
  305. {
  306. listItemHtmlAttributes.Add(
  307. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: lower-alpha;")
  308. );
  309. }
  310. else if (numFmt == "lowerRoman")
  311. {
  312. listItemHtmlAttributes.Add(
  313. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: lower-roman;")
  314. );
  315. }
  316. else if (numFmt == "upperLetter")
  317. {
  318. listItemHtmlAttributes.Add(
  319. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: upper-alpha;")
  320. );
  321. }
  322. else if (numFmt == "upperRoman")
  323. {
  324. listItemHtmlAttributes.Add(
  325. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: upper-roman;")
  326. );
  327. }
  328. else if (numFmt == "custom" && format.StartsWith("0"))
  329. {
  330. listItemHtmlAttributes.Add(
  331. new XAttribute(PtOpenXml.HtmlStyle, "list-style-type: decimal-leading-zero;")
  332. );
  333. }
  334. }
  335. }
  336. var paragraphLevel = ListItemRetriever.GetParagraphLevel(element);
  337. ListItemRetriever.LevelNumbers levelNums = element.Annotation<ListItemRetriever.LevelNumbers>();
  338. string levelNumsString = levelNums
  339. .LevelNumbersArray
  340. .Take(paragraphLevel + 1)
  341. .Select(i => i.ToString() + ".")
  342. .StringConcatenate()
  343. .TrimEnd('.');
  344. ListItemRetriever.ListItemInfo listItemInfo2 = element.Annotation<ListItemRetriever.ListItemInfo>();
  345. var listItemRun = new XElement(W.r,
  346. new XAttribute(PtOpenXml.ListItemRun, levelNumsString),
  347. listItemInfo2 != null ? new XAttribute(PtOpenXml.AbstractNumId, listItemInfo2.AbstractNumId) : null,
  348. element.Attribute(PtOpenXml.FontName),
  349. element.Attribute(PtOpenXml.LanguageType),
  350. listItemRunProps,
  351. new XElement(W.t,
  352. new XAttribute(XNamespace.Xml + "space", "preserve"),
  353. li));
  354. AdjustFontAttributes(wDoc, listItemRun, null, listItemRunProps, settings);
  355. var lvl = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(element));
  356. XElement suffix = new XElement(W.tab);
  357. var su = (string)lvl.Elements(W.suff).Attributes(W.val).FirstOrDefault();
  358. if (su == "space")
  359. suffix = new XElement(W.t,
  360. new XAttribute(XNamespace.Xml + "space", "preserve"),
  361. " ");
  362. else if (su == "nothing")
  363. suffix = null;
  364. var jc = (string)lvl.Elements(W.lvlJc).Attributes(W.val).FirstOrDefault();
  365. if (jc == "right")
  366. {
  367. var accumulatedParaProps = element.Element(PtOpenXml.pPr);
  368. var hangingAtt = accumulatedParaProps.Elements(W.ind).Attributes(W.hanging).FirstOrDefault();
  369. if (hangingAtt == null)
  370. {
  371. var listItemRunLength = WordprocessingMLUtil.CalcWidthOfRunInTwips(listItemRun);
  372. var ind = accumulatedParaProps.Element(W.ind);
  373. if (ind == null)
  374. {
  375. ind = new XElement(W.ind);
  376. accumulatedParaProps.Add(ind);
  377. }
  378. ind.Add(new XAttribute(W.hanging, listItemRunLength.ToString()));
  379. }
  380. else
  381. {
  382. var hanging = (int)hangingAtt;
  383. var listItemRunLength = WordprocessingMLUtil.CalcWidthOfRunInTwips(listItemRun);
  384. hanging += listItemRunLength; // should be width of list item, in twips
  385. hangingAtt.Value = hanging.ToString();
  386. }
  387. }
  388. else if (jc == "center")
  389. {
  390. var accumulatedParaProps = element.Element(PtOpenXml.pPr);
  391. var hangingAtt = accumulatedParaProps.Elements(W.ind).Attributes(W.hanging).FirstOrDefault();
  392. if (hangingAtt == null)
  393. {
  394. var listItemRunLength = WordprocessingMLUtil.CalcWidthOfRunInTwips(listItemRun);
  395. var ind = accumulatedParaProps.Element(W.ind);
  396. if (ind == null)
  397. {
  398. ind = new XElement(W.ind);
  399. accumulatedParaProps.Add(ind);
  400. }
  401. ind.Add(new XAttribute(W.hanging, (listItemRunLength / 2).ToString()));
  402. }
  403. else
  404. {
  405. var hanging = (int)hangingAtt;
  406. var listItemRunLength = WordprocessingMLUtil.CalcWidthOfRunInTwips(listItemRun);
  407. hanging += (listItemRunLength / 2); // should be half of width of list item, in twips
  408. hangingAtt.Value = hanging.ToString();
  409. }
  410. }
  411. AddTabAtLeftIndent(element.Element(PtOpenXml.pPr));
  412. XElement tabRun = suffix != null ?
  413. new XElement(W.r,
  414. new XAttribute(PtOpenXml.ListItemRun, levelNumsString),
  415. listItemRunProps,
  416. suffix) : null;
  417. bool isDeleted = false;
  418. bool isInserted = false;
  419. XAttribute authorAtt = null;
  420. XAttribute dateAtt = null;
  421. var paraDelElement = newParaProps
  422. .Elements(W.rPr)
  423. .Elements(W.del)
  424. .FirstOrDefault();
  425. if (paraDelElement != null)
  426. {
  427. isDeleted = true;
  428. authorAtt = paraDelElement.Attribute(W.author);
  429. dateAtt = paraDelElement.Attribute(W.date);
  430. }
  431. var paraInsElement = newParaProps
  432. .Elements(W.rPr)
  433. .Elements(W.ins)
  434. .FirstOrDefault();
  435. if (paraInsElement != null)
  436. {
  437. isInserted = true;
  438. authorAtt = paraInsElement.Attribute(W.author);
  439. dateAtt = paraInsElement.Attribute(W.date);
  440. }
  441. var paragraphBefore = element
  442. .SiblingsBeforeSelfReverseDocumentOrder()
  443. .FirstOrDefault();
  444. if (paragraphBefore != null)
  445. {
  446. var paraInsElement2 = paragraphBefore
  447. .Elements(W.pPr)
  448. .Elements(W.rPr)
  449. .Elements(W.ins)
  450. .FirstOrDefault();
  451. if (paraInsElement2 != null)
  452. {
  453. isInserted = true;
  454. authorAtt = paraInsElement2.Attribute(W.author);
  455. dateAtt = paraInsElement2.Attribute(W.date);
  456. }
  457. }
  458. #if false
  459. <w:p w14:paraId="448CD560"
  460. w14:textId="77777777"
  461. w:rsidR="003C33D5"
  462. w:rsidRDefault="003C33D5"
  463. w:rsidP="003C33D5">
  464. <w:pPr>
  465. <w:pStyle w:val="ListParagraph"/>
  466. <w:numPr>
  467. <w:ilvl w:val="0"/>
  468. <w:numId w:val="1"/>
  469. </w:numPr>
  470. <w:pPrChange w:id="4"
  471. w:author="e"
  472. w:date="2020-02-07T18:26:00Z">
  473. <w:pPr/>
  474. </w:pPrChange>
  475. </w:pPr>
  476. <w:r>
  477. <w:t>When you click Online Video, you can paste in the embed code for the video you want to add.</w:t>
  478. </w:r>
  479. </w:p>
  480. #endif
  481. var pPrChange = element
  482. .Elements(W.pPr)
  483. .Elements(W.pPrChange)
  484. .FirstOrDefault();
  485. if (pPrChange != null)
  486. {
  487. authorAtt = pPrChange.Attribute(W.author);
  488. dateAtt = pPrChange.Attribute(W.date);
  489. var thisNumPr = element
  490. .Elements(W.pPr)
  491. .Elements(W.numPr)
  492. .FirstOrDefault();
  493. var thisNumPrChange = pPrChange
  494. .Elements(W.numPr)
  495. .FirstOrDefault();
  496. if (thisNumPr != null && thisNumPrChange == null)
  497. isInserted = true;
  498. if (thisNumPr == null && thisNumPrChange != null)
  499. isDeleted = true;
  500. }
  501. if (isDeleted)
  502. {
  503. // convert listItemRun and tabRun to their deleted equivalents
  504. var highestId = wDoc
  505. .MainDocumentPart
  506. .GetXDocument()
  507. .Descendants()
  508. .Attributes(W.id)
  509. .Select(id =>
  510. {
  511. int numId;
  512. if (int.TryParse((string)id, out numId))
  513. return numId;
  514. else
  515. return 0;
  516. })
  517. .Max();
  518. listItemRun = new XElement(W.del,
  519. new XAttribute(W.id, highestId + 1),
  520. authorAtt,
  521. dateAtt,
  522. (XElement)TransformToDeleted(listItemRun));
  523. tabRun = new XElement(W.del,
  524. new XAttribute(W.id, highestId + 2),
  525. authorAtt,
  526. dateAtt,
  527. (XElement)TransformToDeleted(tabRun));
  528. }
  529. else
  530. {
  531. if (isInserted)
  532. {
  533. // convert listItemRun and tabRun to their inserted equivalents
  534. var highestId = wDoc
  535. .MainDocumentPart
  536. .GetXDocument()
  537. .Descendants()
  538. .Attributes(W.id)
  539. .Select(id =>
  540. {
  541. int numId;
  542. if (int.TryParse((string)id, out numId))
  543. return numId;
  544. else
  545. return 0;
  546. })
  547. .Max();
  548. listItemRun = new XElement(W.ins,
  549. new XAttribute(W.id, highestId + 1),
  550. authorAtt,
  551. dateAtt,
  552. listItemRun);
  553. tabRun = new XElement(W.ins,
  554. new XAttribute(W.id, highestId + 2),
  555. authorAtt,
  556. dateAtt,
  557. tabRun);
  558. }
  559. }
  560. XElement newPara = new XElement(W.p,
  561. element.Attribute(PtOpenXml.FontName),
  562. element.Attribute(PtOpenXml.LanguageType),
  563. element.Attribute(PtOpenXml.Unid),
  564. new XAttribute(PtOpenXml.AbstractNumId, abstractNumId),
  565. listItemHtmlAttributes,
  566. newParaProps,
  567. listItemRun,
  568. tabRun,
  569. element.Elements().Where(e => e.Name != W.pPr).Select(n => NormalizeListItemsTransform(fai, wDoc, n, settings)));
  570. return newPara;
  571. }
  572. }
  573. return new XElement(element.Name,
  574. element.Attributes(),
  575. element.Nodes().Select(n => NormalizeListItemsTransform(fai, wDoc, n, settings)));
  576. }
  577. return node;
  578. }
  579. private static object TransformToDeleted(XNode node)
  580. {
  581. XElement element = node as XElement;
  582. if (element != null)
  583. {
  584. if (element.Name == W.t)
  585. return new XElement(W.delText, element.Value);
  586. return new XElement(element.Name,
  587. element.Attributes(),
  588. element.Nodes().Select(n => TransformToDeleted(n)));
  589. }
  590. return node;
  591. }
  592. private static void AddTabAtLeftIndent(XElement pPr)
  593. {
  594. int left = 0;
  595. var ind = pPr.Element(W.ind);
  596. // todo need to handle W.start
  597. if (pPr.Attribute(W.left) != null)
  598. left = (int)pPr.Attribute(W.left);
  599. var tabs = pPr.Element(W.tabs);
  600. if (tabs == null)
  601. {
  602. tabs = new XElement(W.tabs);
  603. pPr.Add(tabs);
  604. }
  605. var tabAtLeft = tabs.Elements(W.tab).FirstOrDefault(t => WordprocessingMLUtil.StringToTwips((string)t.Attribute(W.pos)) == left);
  606. if (tabAtLeft == null)
  607. {
  608. tabs.Add(
  609. new XElement(W.tab,
  610. new XAttribute(W.val, "left"),
  611. new XAttribute(W.pos, left)));
  612. }
  613. }
  614. public static XName[] PtNamesToKeep = new[] {
  615. PtOpenXml.FontName,
  616. PtOpenXml.AbstractNumId,
  617. PtOpenXml.HtmlStructure,
  618. PtOpenXml.HtmlStyle,
  619. PtOpenXml.StyleName,
  620. PtOpenXml.LanguageType,
  621. PtOpenXml.ListItemRun,
  622. PtOpenXml.Unid,
  623. };
  624. public static void NormalizePropsForPart(XDocument pxd, FormattingAssemblerSettings settings)
  625. {
  626. if (settings.CreateHtmlConverterAnnotationAttributes)
  627. {
  628. pxd.Root.Descendants().Attributes().Where(d => d.Name.Namespace == PtOpenXml.pt &&
  629. !PtNamesToKeep.Contains(d.Name)).Remove();
  630. if (pxd.Root.Attribute(XNamespace.Xmlns + "pt14") == null)
  631. pxd.Root.Add(new XAttribute(XNamespace.Xmlns + "pt14", PtOpenXml.pt.NamespaceName));
  632. if (pxd.Root.Attribute(XNamespace.Xmlns + "mc") == null)
  633. pxd.Root.Add(new XAttribute(XNamespace.Xmlns + "mc", MC.mc.NamespaceName));
  634. XAttribute mci = pxd.Root.Attribute(MC.Ignorable);
  635. if (mci != null)
  636. {
  637. if (!pxd.Root.Attribute(MC.Ignorable).Value.Contains("pt14"))
  638. {
  639. var ig = pxd.Root.Attribute(MC.Ignorable).Value + " pt14";
  640. mci.Value = ig;
  641. }
  642. }
  643. else
  644. {
  645. pxd.Root.Add(new XAttribute(MC.Ignorable, "pt14"));
  646. }
  647. }
  648. else
  649. {
  650. pxd.Root.Descendants().Attributes().Where(d => d.Name.Namespace == PtOpenXml.pt).Remove();
  651. }
  652. var runProps = pxd.Root.Descendants(PtOpenXml.rPr).ToList();
  653. foreach (var item in runProps)
  654. {
  655. XElement newRunProps = new XElement(W.rPr,
  656. item.Attributes(),
  657. item.Elements());
  658. XElement parent = item.Parent;
  659. if (parent.Name == W.p)
  660. {
  661. XElement existingParaProps = parent.Element(W.pPr);
  662. if (existingParaProps == null)
  663. {
  664. existingParaProps = new XElement(W.pPr);
  665. parent.Add(existingParaProps);
  666. }
  667. XElement existingRunProps = existingParaProps.Element(W.rPr);
  668. if (existingRunProps != null)
  669. {
  670. if (!settings.RemoveStyleNamesFromParagraphAndRunProperties)
  671. {
  672. if (newRunProps.Element(W.rStyle) == null)
  673. newRunProps.Add(existingRunProps.Element(W.rStyle));
  674. }
  675. existingRunProps.ReplaceWith(newRunProps);
  676. }
  677. else
  678. existingParaProps.Add(newRunProps);
  679. }
  680. else
  681. {
  682. XElement existingRunProps = parent.Element(W.rPr);
  683. if (existingRunProps != null)
  684. {
  685. if (!settings.RemoveStyleNamesFromParagraphAndRunProperties)
  686. {
  687. if (newRunProps.Element(W.rStyle) == null)
  688. newRunProps.Add(existingRunProps.Element(W.rStyle));
  689. }
  690. existingRunProps.ReplaceWith(newRunProps);
  691. }
  692. else
  693. parent.Add(newRunProps);
  694. }
  695. }
  696. var paraProps = pxd.Root.Descendants(PtOpenXml.pPr).ToList();
  697. foreach (var item in paraProps)
  698. {
  699. var paraRunProps = item.Parent.Elements(W.pPr).Elements(W.rPr).FirstOrDefault();
  700. var merged = MergeStyleElement(item.Element(W.rPr), paraRunProps);
  701. if (!settings.RemoveStyleNamesFromParagraphAndRunProperties)
  702. {
  703. if (merged.Element(W.rStyle) == null)
  704. {
  705. merged.Add(paraRunProps.Element(W.rStyle));
  706. }
  707. }
  708. XElement newParaProps = new XElement(W.pPr,
  709. item.Attributes(),
  710. item.Elements().Where(e => e.Name != W.rPr),
  711. merged);
  712. XElement para = item.Parent;
  713. XElement existingParaProps = para.Element(W.pPr);
  714. if (existingParaProps != null)
  715. {
  716. if (!settings.RemoveStyleNamesFromParagraphAndRunProperties)
  717. {
  718. if (newParaProps.Element(W.pStyle) == null)
  719. newParaProps.Add(existingParaProps.Element(W.pStyle));
  720. }
  721. existingParaProps.ReplaceWith(newParaProps);
  722. }
  723. else
  724. para.Add(newParaProps);
  725. }
  726. var tblProps = pxd.Root.Descendants(PtOpenXml.tblPr).ToList();
  727. foreach (var item in tblProps)
  728. {
  729. XElement newTblProps = new XElement(item);
  730. newTblProps.Name = W.tblPr;
  731. XElement table = item.Parent;
  732. XElement existingTableProps = table.Element(W.tblPr);
  733. if (existingTableProps != null)
  734. existingTableProps.ReplaceWith(newTblProps);
  735. else
  736. table.AddFirst(newTblProps);
  737. }
  738. var trProps = pxd.Root.Descendants(PtOpenXml.trPr).ToList();
  739. foreach (var item in trProps)
  740. {
  741. XElement newTrProps = new XElement(item);
  742. newTrProps.Name = W.trPr;
  743. XElement row = item.Parent;
  744. XElement existingRowProps = row.Element(W.trPr);
  745. if (existingRowProps != null)
  746. existingRowProps.ReplaceWith(newTrProps);
  747. else
  748. row.AddFirst(newTrProps);
  749. }
  750. var tcProps = pxd.Root.Descendants(PtOpenXml.tcPr).ToList();
  751. foreach (var item in tcProps)
  752. {
  753. XElement newTcProps = new XElement(item);
  754. newTcProps.Name = W.tcPr;
  755. XElement row = item.Parent;
  756. XElement existingRowProps = row.Element(W.tcPr);
  757. if (existingRowProps != null)
  758. existingRowProps.ReplaceWith(newTcProps);
  759. else
  760. row.AddFirst(newTcProps);
  761. }
  762. pxd.Root.Descendants(W.numPr).Remove();
  763. if (settings.RemoveStyleNamesFromParagraphAndRunProperties)
  764. {
  765. pxd.Root.Descendants(W.pStyle).Where(ps => ps.Parent.Name == W.pPr).Remove();
  766. pxd.Root.Descendants(W.rStyle).Where(ps => ps.Parent.Name == W.rPr).Remove();
  767. }
  768. pxd.Root.Descendants(W.tblStyle).Where(ps => ps.Parent.Name == W.tblPr).Remove();
  769. pxd.Root.Descendants().Where(d => d.Name.Namespace == PtOpenXml.pt).Remove();
  770. if (settings.OrderElementsPerStandard)
  771. {
  772. XElement newRoot = (XElement)WordprocessingMLUtil.WmlOrderElementsPerStandard(pxd.Root);
  773. pxd.Root.ReplaceWith(newRoot);
  774. }
  775. }
  776. private static void AssembleListItemInformation(WordprocessingDocument wordDoc, ListItemRetrieverSettings settings)
  777. {
  778. foreach (var part in wordDoc.ContentParts())
  779. {
  780. XDocument xDoc = part.GetXDocument();
  781. foreach (var para in xDoc.Descendants(W.p))
  782. {
  783. ListItemRetriever.RetrieveListItem(wordDoc, para, settings);
  784. }
  785. }
  786. }
  787. private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XElement rootElement, FormattingAssemblerSettings settings)
  788. {
  789. XElement globalDefaultParaProps = null;
  790. XElement globalDefaultParaPropsAsDefined = null;
  791. XElement globalDefaultRunProps = null;
  792. XElement globalDefaultRunPropsAsDefined = null;
  793. XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  794. var defaultParaStyleName = (string)sXDoc
  795. .Root
  796. .Elements(W.style)
  797. .Where(st => (string)st.Attribute(W.type) == "paragraph" && st.Attribute(W._default).ToBoolean() == true)
  798. .Attributes(W.styleId)
  799. .FirstOrDefault();
  800. var defaultCharStyleName = (string)sXDoc
  801. .Root
  802. .Elements(W.style)
  803. .Where(st => (string)st.Attribute(W.type) == "character" && st.Attribute(W._default).ToBoolean() == true)
  804. .Attributes(W.styleId)
  805. .FirstOrDefault();
  806. XElement docDefaults = sXDoc.Root.Element(W.docDefaults);
  807. if (docDefaults != null)
  808. {
  809. globalDefaultParaPropsAsDefined = docDefaults.Elements(W.pPrDefault).Elements(W.pPr)
  810. .FirstOrDefault();
  811. if (globalDefaultParaPropsAsDefined == null)
  812. globalDefaultParaPropsAsDefined = new XElement(W.pPr,
  813. new XElement(W.rPr));
  814. globalDefaultRunPropsAsDefined = docDefaults.Elements(W.rPrDefault).Elements(W.rPr)
  815. .FirstOrDefault();
  816. if (globalDefaultRunPropsAsDefined == null)
  817. globalDefaultRunPropsAsDefined = new XElement(W.rPr);
  818. if (globalDefaultRunPropsAsDefined.Element(W.rFonts) == null)
  819. globalDefaultRunPropsAsDefined.Add(
  820. new XElement(W.rFonts,
  821. new XAttribute(W.ascii, "Times New Roman"),
  822. new XAttribute(W.hAnsi, "Times New Roman"),
  823. new XAttribute(W.cs, "Times New Roman")));
  824. if (globalDefaultRunPropsAsDefined.Element(W.sz) == null)
  825. globalDefaultRunPropsAsDefined.Add(
  826. new XElement(W.sz,
  827. new XAttribute(W.val, "20")));
  828. if (globalDefaultRunPropsAsDefined.Element(W.szCs) == null)
  829. globalDefaultRunPropsAsDefined.Add(
  830. new XElement(W.szCs,
  831. new XAttribute(W.val, "20")));
  832. var runPropsForGlobalDefaultParaProps = MergeStyleElement(globalDefaultRunPropsAsDefined, globalDefaultParaPropsAsDefined.Element(W.rPr));
  833. globalDefaultParaProps = new XElement(globalDefaultParaPropsAsDefined.Name,
  834. globalDefaultParaPropsAsDefined.Attributes(),
  835. globalDefaultParaPropsAsDefined.Elements().Where(e => e.Name != W.rPr),
  836. runPropsForGlobalDefaultParaProps);
  837. globalDefaultRunProps = MergeStyleElement(globalDefaultParaPropsAsDefined.Element(W.rPr), globalDefaultRunPropsAsDefined);
  838. }
  839. var rPr = new XElement(W.rPr,
  840. new XElement(W.rFonts,
  841. new XAttribute(W.ascii, "Times New Roman"),
  842. new XAttribute(W.hAnsi, "Times New Roman"),
  843. new XAttribute(W.cs, "Times New Roman")),
  844. new XElement(W.sz,
  845. new XAttribute(W.val, "20")),
  846. new XElement(W.szCs,
  847. new XAttribute(W.val, "20")));
  848. if (globalDefaultParaProps == null)
  849. globalDefaultParaProps = new XElement(W.pPr, rPr);
  850. if (globalDefaultRunProps == null)
  851. globalDefaultRunProps = rPr;
  852. XElement ptGlobalDefaultParaProps = new XElement(globalDefaultParaProps);
  853. XElement ptGlobalDefaultRunProps = new XElement(globalDefaultRunProps);
  854. ptGlobalDefaultParaProps.Name = PtOpenXml.pPr;
  855. ptGlobalDefaultRunProps.Name = PtOpenXml.rPr;
  856. var parasAndRuns = rootElement.Descendants().Where(d =>
  857. {
  858. return d.Name == W.p || d.Name == W.r;
  859. });
  860. if (settings.CreateHtmlConverterAnnotationAttributes)
  861. {
  862. foreach (var d in parasAndRuns)
  863. {
  864. if (d.Name == W.p)
  865. {
  866. var pStyle = (string)d.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
  867. if (pStyle == null)
  868. pStyle = defaultParaStyleName;
  869. if (pStyle != null)
  870. {
  871. if (d.Attribute(PtOpenXml.StyleName) != null)
  872. d.Attribute(PtOpenXml.StyleName).Value = pStyle;
  873. else
  874. d.Add(new XAttribute(PtOpenXml.StyleName, pStyle));
  875. }
  876. d.Add(ptGlobalDefaultParaProps);
  877. }
  878. else
  879. {
  880. var rStyle = (string)d.Elements(W.rPr).Elements(W.rStyle).Attributes(W.val).FirstOrDefault();
  881. if (rStyle == null)
  882. rStyle = defaultCharStyleName;
  883. if (rStyle != null)
  884. {
  885. if (d.Attribute(PtOpenXml.StyleName) != null)
  886. d.Attribute(PtOpenXml.StyleName).Value = rStyle;
  887. else
  888. d.Add(new XAttribute(PtOpenXml.StyleName, rStyle));
  889. }
  890. d.Add(ptGlobalDefaultRunProps);
  891. }
  892. }
  893. }
  894. else
  895. {
  896. foreach (var d in parasAndRuns)
  897. {
  898. if (d.Name == W.p)
  899. {
  900. d.Add(ptGlobalDefaultParaProps);
  901. }
  902. else
  903. {
  904. d.Add(ptGlobalDefaultRunProps);
  905. }
  906. }
  907. }
  908. }
  909. private static XElement BlankTcBorders = new XElement(W.tcBorders,
  910. new XElement(W.top, new XAttribute(W.val, "nil")),
  911. new XElement(W.left, new XAttribute(W.val, "nil")),
  912. new XElement(W.bottom, new XAttribute(W.val, "nil")),
  913. new XElement(W.right, new XAttribute(W.val, "nil")));
  914. private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, XElement rootElement)
  915. {
  916. XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  917. foreach (var tbl in rootElement.Descendants(W.tbl))
  918. {
  919. string tblStyleName = (string)tbl.Elements(W.tblPr).Elements(W.tblStyle).Attributes(W.val).FirstOrDefault();
  920. if (tblStyleName != null)
  921. {
  922. XElement style = TableStyleRollup(wDoc, tblStyleName);
  923. // annotate table with table style, in PowerTools namespace
  924. style.Name = PtOpenXml.style;
  925. tbl.Add(style);
  926. // merge tblPr in table style with tblPr of the table
  927. // annnotate in PowerTools namespace
  928. XElement tblPr2 = style.Element(W.tblPr);
  929. XElement tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr2, true);
  930. if (tblPr3 != null)
  931. {
  932. XElement newTblPr = new XElement(tblPr3);
  933. newTblPr.Name = PtOpenXml.pt + "tblPr";
  934. tbl.Add(newTblPr);
  935. }
  936. AddTcPrPtToEveryCell(tbl);
  937. AddOuterBorders(tbl, style);
  938. var tableTcPr = style.Element(W.tcPr);
  939. if (tableTcPr != null)
  940. {
  941. foreach (var row in tbl.Elements(W.tr))
  942. {
  943. foreach (var cell in row.Elements(W.tc))
  944. {
  945. bool tcPrPtExists = false;
  946. var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr");
  947. if (tcPrPt != null)
  948. tcPrPtExists = true;
  949. else
  950. tcPrPt = new XElement(W.tcPr);
  951. tcPrPt = MergeStyleElement(tableTcPr, tcPrPt);
  952. var newTcPrPt = new XElement(tcPrPt);
  953. newTcPrPt.Name = PtOpenXml.tcPr;
  954. if (tcPrPtExists)
  955. cell.Element(PtOpenXml.tcPr).ReplaceWith(newTcPrPt);
  956. else
  957. cell.Add(newTcPrPt);
  958. }
  959. }
  960. }
  961. // Iterate through every row and cell in the table, rolling up row properties and cell properties
  962. // as appropriate per the cnfStyle element, then replacing the row and cell properties
  963. foreach (var row in tbl.Elements(W.tr))
  964. {
  965. XElement trPr2 = null;
  966. trPr2 = style.Element(W.trPr);
  967. if (trPr2 == null)
  968. trPr2 = new XElement(W.trPr);
  969. XElement rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault();
  970. if (rowCnf != null)
  971. {
  972. foreach (var ot in TableStyleOverrideTypes)
  973. {
  974. XName attName = TableStyleOverrideXNameMap[ot];
  975. if (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true)
  976. {
  977. XElement o = style
  978. .Elements(W.tblStylePr)
  979. .Where(tsp => (string)tsp.Attribute(W.type) == ot)
  980. .FirstOrDefault();
  981. if (o != null)
  982. {
  983. XElement ottrPr = o.Element(W.trPr);
  984. trPr2 = MergeStyleElement(ottrPr, trPr2);
  985. }
  986. }
  987. }
  988. }
  989. trPr2 = MergeStyleElement(row.Element(W.trPr), trPr2);
  990. if (trPr2.HasElements)
  991. {
  992. trPr2.Name = PtOpenXml.pt + "trPr";
  993. row.Add(trPr2);
  994. }
  995. }
  996. foreach (var ot in TableStyleOverrideTypes)
  997. {
  998. XName attName = TableStyleOverrideXNameMap[ot];
  999. if (attName == W.oddHBand ||
  1000. attName == W.evenHBand ||
  1001. attName == W.firstRow ||
  1002. attName == W.lastRow)
  1003. {
  1004. foreach (var row in tbl.Elements(W.tr))
  1005. {
  1006. XElement rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault();
  1007. if (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true)
  1008. {
  1009. XElement o = style
  1010. .Elements(W.tblStylePr)
  1011. .Where(tsp => (string)tsp.Attribute(W.type) == ot)
  1012. .FirstOrDefault();
  1013. if (o != null)
  1014. {
  1015. foreach (var cell in row.Elements(W.tc))
  1016. {
  1017. bool tcPrPtExists = false;
  1018. var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr");
  1019. if (tcPrPt != null)
  1020. tcPrPtExists = true;
  1021. else
  1022. tcPrPt = new XElement(W.tcPr);
  1023. tcPrPt = MergeStyleElement(o.Element(W.tcPr), tcPrPt);
  1024. var newTcPrPt = new XElement(tcPrPt);
  1025. newTcPrPt.Name = PtOpenXml.pt + "tcPr";
  1026. if (tcPrPtExists)
  1027. cell.Element(PtOpenXml.pt + "tcPr").ReplaceWith(newTcPrPt);
  1028. else
  1029. cell.Add(newTcPrPt);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. }
  1035. else if (attName == W.firstColumn ||
  1036. attName == W.lastColumn ||
  1037. attName == W.oddVBand ||
  1038. attName == W.evenVBand)
  1039. {
  1040. foreach (var row in tbl.Elements(W.tr))
  1041. {
  1042. foreach (var cell in row.Elements(W.tc))
  1043. {
  1044. ApplyCndFmtToCell(style, ot, attName, cell);
  1045. }
  1046. }
  1047. }
  1048. else if (attName == W.firstRowLastColumn)
  1049. {
  1050. var row = tbl.Elements(W.tr).FirstOrDefault();
  1051. if (row != null)
  1052. {
  1053. var cell = row.Elements(W.tc).LastOrDefault();
  1054. if (cell != null)
  1055. ApplyCndFmtToCell(style, ot, attName, cell);
  1056. }
  1057. }
  1058. else if (attName == W.firstRowFirstColumn)
  1059. {
  1060. var row = tbl.Elements(W.tr).FirstOrDefault();
  1061. if (row != null)
  1062. {
  1063. var cell = row.Elements(W.tc).FirstOrDefault();
  1064. if (cell != null)
  1065. ApplyCndFmtToCell(style, ot, attName, cell);
  1066. }
  1067. }
  1068. else if (attName == W.lastRowLastColumn)
  1069. {
  1070. var row = tbl.Elements(W.tr).LastOrDefault();
  1071. if (row != null)
  1072. {
  1073. var cell = row.Elements(W.tc).LastOrDefault();
  1074. if (cell != null)
  1075. ApplyCndFmtToCell(style, ot, attName, cell);
  1076. }
  1077. }
  1078. else if (attName == W.lastRowFirstColumn)
  1079. {
  1080. var row = tbl.Elements(W.tr).LastOrDefault();
  1081. if (row != null)
  1082. {
  1083. var cell = row.Elements(W.tc).FirstOrDefault();
  1084. if (cell != null)
  1085. ApplyCndFmtToCell(style, ot, attName, cell);
  1086. }
  1087. }
  1088. }
  1089. ProcessInnerBorders(tbl, style);
  1090. }
  1091. else
  1092. {
  1093. var tblPr = new XElement(W.tblPr);
  1094. XElement tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr, true);
  1095. if (tblPr3 != null)
  1096. {
  1097. XElement newTblPr = new XElement(tblPr3);
  1098. newTblPr.Name = PtOpenXml.pt + "tblPr";
  1099. tbl.Add(newTblPr);
  1100. }
  1101. AddTcPrPtToEveryCell(tbl);
  1102. }
  1103. RollInDirectFormatting(tbl); // it is important that this is last. This merges in direct formatting.
  1104. }
  1105. }
  1106. private static void AddTcPrPtToEveryCell(XElement tbl)
  1107. {
  1108. foreach (var row in tbl.Elements(W.tr))
  1109. {
  1110. foreach (var cell in row.Elements(W.tc))
  1111. {
  1112. var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr");
  1113. if (tcPrPt != null)
  1114. continue;
  1115. tcPrPt = new XElement(PtOpenXml.pt + "tcPr",
  1116. new XElement(W.tcBorders));
  1117. cell.Add(tcPrPt);
  1118. }
  1119. }
  1120. }
  1121. private static void ApplyCndFmtToCell(XElement style, string ot, XName attName, XElement cell)
  1122. {
  1123. XElement cellCnf = cell.Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault();
  1124. if (cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true)
  1125. {
  1126. XElement o = style
  1127. .Elements(W.tblStylePr)
  1128. .Where(tsp => (string)tsp.Attribute(W.type) == ot)
  1129. .FirstOrDefault();
  1130. if (o != null)
  1131. {
  1132. bool tcPrPtExists = false;
  1133. var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr");
  1134. if (tcPrPt != null)
  1135. tcPrPtExists = true;
  1136. else
  1137. tcPrPt = new XElement(W.tcPr);
  1138. tcPrPt = MergeStyleElement(o.Element(W.tcPr), tcPrPt);
  1139. var newTcPrPt = new XElement(tcPrPt);
  1140. newTcPrPt.Name = PtOpenXml.pt + "tcPr";
  1141. if (tcPrPtExists)
  1142. cell.Element(PtOpenXml.pt + "tcPr").ReplaceWith(newTcPrPt);
  1143. else
  1144. cell.Add(newTcPrPt);
  1145. }
  1146. }
  1147. }
  1148. private static void RollInDirectFormatting(XElement tbl)
  1149. {
  1150. foreach (var row in tbl.Elements(W.tr))
  1151. {
  1152. foreach (var cell in row.Elements(W.tc))
  1153. {
  1154. var ptTcPr = cell.Element(PtOpenXml.pt + "tcPr");
  1155. var tcPr = cell.Element(W.tcPr);
  1156. var mTcPr = MergeStyleElement(tcPr, ptTcPr);
  1157. if (mTcPr == null)
  1158. {
  1159. mTcPr = new XElement(PtOpenXml.pt + "tcPr");
  1160. cell.Add(tcPr);
  1161. }
  1162. var newTcPr = new XElement(mTcPr);
  1163. newTcPr.Name = PtOpenXml.pt + "tcPr";
  1164. var existing = cell.Element(PtOpenXml.pt + "tcPr");
  1165. if (existing != null)
  1166. existing.ReplaceWith(newTcPr);
  1167. else
  1168. cell.Add(newTcPr);
  1169. }
  1170. }
  1171. var tblBorders = tbl.Elements(PtOpenXml.pt + "tblPr").Elements(W.tblBorders).FirstOrDefault();
  1172. if (tblBorders != null && tblBorders.Attribute(PtOpenXml.pt + "fromDirect") != null)
  1173. {
  1174. ApplyTblBordersToTable(tbl, tblBorders);
  1175. ProcessInnerBordersPerTblBorders(tbl, tblBorders);
  1176. }
  1177. }
  1178. private static void ApplyTblBordersToTable(XElement tbl, XElement tblBorders)
  1179. {
  1180. var top = tblBorders.Element(W.top);
  1181. if (top != null)
  1182. {
  1183. var firstRow = tbl.Elements(W.tr).FirstOrDefault();
  1184. if (firstRow != null)
  1185. {
  1186. foreach (var cell in firstRow.Elements(W.tc))
  1187. {
  1188. var cellTcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1189. if (cellTcBorders != null)
  1190. {
  1191. var cellTop = cellTcBorders.Element(W.top);
  1192. if (cellTop == null)
  1193. cellTcBorders.Add(top);
  1194. else
  1195. cellTop.ReplaceAttributes(top.Attributes());
  1196. }
  1197. }
  1198. }
  1199. }
  1200. var bottom = tblBorders.Element(W.bottom);
  1201. if (bottom != null)
  1202. {
  1203. var lastRow = tbl.Elements(W.tr).LastOrDefault();
  1204. if (lastRow != null)
  1205. {
  1206. foreach (var cell in lastRow.Elements(W.tc))
  1207. {
  1208. var cellTcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1209. if (cellTcBorders != null)
  1210. {
  1211. var cellBottom = cellTcBorders.Element(W.bottom);
  1212. if (cellBottom == null)
  1213. cellTcBorders.Add(bottom);
  1214. else
  1215. cellBottom.ReplaceAttributes(bottom.Attributes());
  1216. }
  1217. }
  1218. }
  1219. }
  1220. foreach (var row in tbl.Elements(W.tr))
  1221. {
  1222. var left = tblBorders.Element(W.left);
  1223. if (left != null)
  1224. {
  1225. var firstCell = row.Elements(W.tc).FirstOrDefault();
  1226. if (firstCell != null)
  1227. {
  1228. var cellTcBorders = firstCell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1229. if (cellTcBorders != null)
  1230. {
  1231. var firstCellLeft = cellTcBorders.Element(W.left);
  1232. if (firstCellLeft == null)
  1233. cellTcBorders.Add(left);
  1234. else
  1235. firstCellLeft.ReplaceAttributes(left.Attributes());
  1236. }
  1237. }
  1238. }
  1239. var right = tblBorders.Element(W.right);
  1240. if (right != null)
  1241. {
  1242. var lastCell = row.Elements(W.tc).LastOrDefault();
  1243. if (lastCell != null)
  1244. {
  1245. var cellTcBorders = lastCell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1246. if (cellTcBorders != null)
  1247. {
  1248. var lastCellRight = cellTcBorders.Element(W.right);
  1249. if (lastCellRight == null)
  1250. cellTcBorders.Add(right);
  1251. else
  1252. lastCellRight.ReplaceAttributes(right.Attributes());
  1253. }
  1254. }
  1255. }
  1256. }
  1257. }
  1258. private static void AddOuterBorders(XElement tbl, XElement style)
  1259. {
  1260. var tblBorders = tbl.Elements(PtOpenXml.pt + "tblPr").Elements(W.tblBorders).FirstOrDefault();
  1261. if (tblBorders != null)
  1262. ApplyTblBordersToTable(tbl, tblBorders);
  1263. }
  1264. private static void ProcessInnerBorders(XElement tbl, XElement style)
  1265. {
  1266. var tblBorders = tbl.Elements(PtOpenXml.pt + "tblPr").Elements(W.tblBorders).FirstOrDefault();
  1267. if (tblBorders != null)
  1268. ProcessInnerBordersPerTblBorders(tbl, tblBorders);
  1269. foreach (var attName in new[] { W.oddHBand, W.evenHBand, W.firstRow, W.lastRow })
  1270. {
  1271. int rowCount = tbl.Elements(W.tr).Count();
  1272. int lastRow = rowCount - 1;
  1273. XElement insideV = null;
  1274. foreach (var row in tbl.Elements(W.tr))
  1275. {
  1276. var rowCnfStyle = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault();
  1277. if (rowCnfStyle != null)
  1278. {
  1279. var shouldApply = rowCnfStyle.Attribute(attName).ToBoolean();
  1280. if (shouldApply == true)
  1281. {
  1282. var cndType = TableStyleOverrideXNameRevMap[attName];
  1283. var cndStyle = style
  1284. .Elements(W.tblStylePr)
  1285. .FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == cndType);
  1286. if (cndStyle != null)
  1287. {
  1288. var styleTcBorders = cndStyle.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault();
  1289. if (styleTcBorders != null)
  1290. {
  1291. var top = styleTcBorders.Element(W.top);
  1292. var left = styleTcBorders.Element(W.left);
  1293. var bottom = styleTcBorders.Element(W.bottom);
  1294. var right = styleTcBorders.Element(W.right);
  1295. insideV = cndStyle.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.insideV).FirstOrDefault();
  1296. if (insideV != null)
  1297. {
  1298. int lastCol = row.Elements(W.tc).Count() - 1;
  1299. int colIdx = 0;
  1300. foreach (var cell in row.Elements(W.tc))
  1301. {
  1302. var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1303. if (colIdx == 0)
  1304. {
  1305. ResolveInsideWithExisting(tcBorders, insideV, W.right);
  1306. }
  1307. else if (colIdx == lastCol)
  1308. {
  1309. ResolveInsideWithExisting(tcBorders, insideV, W.left);
  1310. }
  1311. else
  1312. {
  1313. ResolveInsideWithExisting(tcBorders, insideV, W.left);
  1314. ResolveInsideWithExisting(tcBorders, insideV, W.right);
  1315. }
  1316. colIdx++;
  1317. }
  1318. }
  1319. }
  1320. }
  1321. }
  1322. }
  1323. }
  1324. }
  1325. foreach (var attName in new[] { W.oddVBand, W.evenVBand, W.firstColumn, W.lastColumn })
  1326. {
  1327. int rowIdx = 0;
  1328. int lastRow = tbl.Elements(W.tr).Count() - 1;
  1329. foreach (var row in tbl.Elements(W.tr))
  1330. {
  1331. foreach (var cell in row.Elements(W.tc))
  1332. {
  1333. var cellCnfStyle = cell.Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault();
  1334. if (cellCnfStyle != null)
  1335. {
  1336. var shouldApply = cellCnfStyle.Attribute(attName).ToBoolean();
  1337. if (shouldApply == true)
  1338. {
  1339. var cndType = TableStyleOverrideXNameRevMap[attName];
  1340. var cndStyle = style
  1341. .Elements(W.tblStylePr)
  1342. .FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == cndType);
  1343. if (cndStyle != null)
  1344. {
  1345. var insideH = cndStyle.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.insideH).FirstOrDefault();
  1346. if (insideH != null)
  1347. {
  1348. var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1349. if (rowIdx == 0)
  1350. {
  1351. ResolveInsideWithExisting(tcBorders, insideH, W.bottom);
  1352. }
  1353. else if (rowIdx == lastRow)
  1354. {
  1355. ResolveInsideWithExisting(tcBorders, insideH, W.top);
  1356. }
  1357. else
  1358. {
  1359. ResolveInsideWithExisting(tcBorders, insideH, W.bottom);
  1360. ResolveInsideWithExisting(tcBorders, insideH, W.top);
  1361. }
  1362. }
  1363. }
  1364. }
  1365. }
  1366. }
  1367. rowIdx++;
  1368. }
  1369. }
  1370. }
  1371. private static void ProcessInnerBordersPerTblBorders(XElement tbl, XElement tblBorders)
  1372. {
  1373. var tblInsideV = tblBorders.Elements(W.insideV).FirstOrDefault();
  1374. if (tblInsideV != null)
  1375. {
  1376. foreach (var row in tbl.Elements(W.tr))
  1377. {
  1378. var lastCell = row.Elements(W.tc).Count() - 1;
  1379. int cellIdx = 0;
  1380. foreach (var cell in row.Elements(W.tc))
  1381. {
  1382. var tcPr = cell.Element(PtOpenXml.pt + "tcPr");
  1383. if (tcPr == null)
  1384. {
  1385. tcPr = new XElement(PtOpenXml.pt + "tcPr");
  1386. cell.Add(tcPr);
  1387. }
  1388. var tcBorders = tcPr.Element(W.tcBorders);
  1389. if (tcBorders == null)
  1390. {
  1391. tcBorders = new XElement(W.tcBorders);
  1392. tcPr.Add(tcBorders);
  1393. }
  1394. if (cellIdx == 0)
  1395. {
  1396. ResolveInsideWithExisting(tcBorders, tblInsideV, W.right);
  1397. }
  1398. else if (cellIdx == lastCell)
  1399. {
  1400. ResolveInsideWithExisting(tcBorders, tblInsideV, W.left);
  1401. }
  1402. else
  1403. {
  1404. ResolveInsideWithExisting(tcBorders, tblInsideV, W.left);
  1405. ResolveInsideWithExisting(tcBorders, tblInsideV, W.right);
  1406. }
  1407. cellIdx++;
  1408. }
  1409. }
  1410. }
  1411. var tblInsideH = tblBorders.Elements(W.insideH).FirstOrDefault();
  1412. if (tblInsideH != null)
  1413. {
  1414. int rowIdx1 = 0;
  1415. int lastRow1 = tbl.Elements(W.tr).Count() - 1;
  1416. foreach (var row in tbl.Elements(W.tr))
  1417. {
  1418. if (rowIdx1 == 0)
  1419. {
  1420. foreach (var cell in row.Elements(W.tc))
  1421. {
  1422. var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1423. ResolveInsideWithExisting(tcBorders, tblInsideH, W.bottom);
  1424. }
  1425. }
  1426. else if (rowIdx1 == lastRow1)
  1427. {
  1428. foreach (var cell in row.Elements(W.tc))
  1429. {
  1430. var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1431. ResolveInsideWithExisting(tcBorders, tblInsideH, W.top);
  1432. }
  1433. }
  1434. else
  1435. {
  1436. foreach (var cell in row.Elements(W.tc))
  1437. {
  1438. var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault();
  1439. ResolveInsideWithExisting(tcBorders, tblInsideH, W.top);
  1440. ResolveInsideWithExisting(tcBorders, tblInsideH, W.bottom);
  1441. }
  1442. }
  1443. rowIdx1++;
  1444. }
  1445. }
  1446. }
  1447. private static void ResolveInsideWithExisting(XElement tcBorders, XElement inside, XName whichSide)
  1448. {
  1449. if (tcBorders.Element(whichSide) != null)
  1450. {
  1451. var newInsideH = ResolveInsideBorder(inside, tcBorders.Element(whichSide));
  1452. tcBorders.Element(whichSide).ReplaceAttributes(
  1453. newInsideH.Attributes());
  1454. }
  1455. else
  1456. {
  1457. tcBorders.Add(
  1458. new XElement(whichSide,
  1459. inside.Attributes()));
  1460. }
  1461. }
  1462. private static Dictionary<string, int> BorderTypePriority = new Dictionary<string, int>()
  1463. {
  1464. { "single", 1 },
  1465. { "thick", 2 },
  1466. { "double", 3 },
  1467. { "dotted", 4 },
  1468. };
  1469. private static Dictionary<string, int> BorderNumber = new Dictionary<string, int>()
  1470. {
  1471. {"single", 1 },
  1472. {"thick", 2 },
  1473. {"double", 3 },
  1474. {"dotted", 4 },
  1475. {"dashed", 5 },
  1476. {"dotDash", 6 },
  1477. {"dotDotDash", 7 },
  1478. {"triple", 8 },
  1479. {"thinThickSmallGap", 9 },
  1480. {"thickThinSmallGap", 10 },
  1481. {"thinThickThinSmallGap", 11 },
  1482. {"thinThickMediumGap", 12 },
  1483. {"thickThinMediumGap", 13 },
  1484. {"thinThickThinMediumGap", 14 },
  1485. {"thinThickLargeGap", 15 },
  1486. {"thickThinLargeGap", 16 },
  1487. {"thinThickThinLargeGap", 17 },
  1488. {"wave", 18 },
  1489. {"doubleWave", 19 },
  1490. {"dashSmallGap", 20 },
  1491. {"dashDotStroked", 21 },
  1492. {"threeDEmboss", 22 },
  1493. {"threeDEngrave", 23 },
  1494. {"outset", 24 },
  1495. {"inset", 25 },
  1496. };
  1497. private static XElement ResolveInsideBorder(XElement inside1, XElement sideToReplace)
  1498. {
  1499. if (inside1 == null && sideToReplace == null)
  1500. return null;
  1501. if (inside1 == null)
  1502. return sideToReplace;
  1503. if (sideToReplace == null)
  1504. return inside1;
  1505. // The following handles the situation where
  1506. // if table innerV is set, and cnd format for first row specifies nill border, then nil border wins.
  1507. // if table innerH is set, and cnd format for first columns specifies nil border, then table innerH wins.
  1508. if (sideToReplace.Name == W.left ||
  1509. sideToReplace.Name == W.right)
  1510. {
  1511. if ((string)inside1.Attribute(W.val) == "nil")
  1512. return inside1;
  1513. if ((string)sideToReplace.Attribute(W.val) == "nil")
  1514. return sideToReplace;
  1515. }
  1516. else
  1517. {
  1518. if ((string)inside1.Attribute(W.val) == "nil")
  1519. return sideToReplace;
  1520. if ((string)sideToReplace.Attribute(W.val) == "nil")
  1521. return inside1;
  1522. }
  1523. var inside1Val = (string)inside1.Attribute(W.val);
  1524. var border1Weight = 1;
  1525. if (BorderNumber.ContainsKey(inside1Val))
  1526. border1Weight = BorderNumber[inside1Val];
  1527. var sideToReplaceVal = (string)sideToReplace.Attribute(W.val);
  1528. var sideToReplaceWeight = 1;
  1529. if (BorderNumber.ContainsKey(sideToReplaceVal))
  1530. sideToReplaceWeight = BorderNumber[sideToReplaceVal];
  1531. if (border1Weight != sideToReplaceWeight)
  1532. {
  1533. if (border1Weight < sideToReplaceWeight)
  1534. return sideToReplace;
  1535. else
  1536. return inside1;
  1537. }
  1538. if ((int)inside1.Attribute(W.sz) > (int)sideToReplace.Attribute(W.sz))
  1539. return inside1;
  1540. if ((int)sideToReplace.Attribute(W.sz) > (int)inside1.Attribute(W.sz))
  1541. return sideToReplace;
  1542. if (BorderTypePriority.ContainsKey(inside1Val) &&
  1543. BorderTypePriority.ContainsKey(sideToReplaceVal))
  1544. {
  1545. var inside1Pri = BorderTypePriority[inside1Val];
  1546. var inside2Pri = BorderTypePriority[sideToReplaceVal];
  1547. if (inside1Pri > inside2Pri)
  1548. return inside1;
  1549. if (inside2Pri > inside1Pri)
  1550. return sideToReplace;
  1551. }
  1552. if ((int)inside1.Attribute(W.sz) > (int)sideToReplace.Attribute(W.sz))
  1553. return inside1;
  1554. if ((int)sideToReplace.Attribute(W.sz) > (int)inside1.Attribute(W.sz))
  1555. return sideToReplace;
  1556. var color1str = (string)inside1.Attribute(W.color);
  1557. if (color1str == "auto")
  1558. color1str = "000000";
  1559. var color2str = (string)sideToReplace.Attribute(W.color);
  1560. if (color2str == "auto")
  1561. color2str = "000000";
  1562. if (color1str != null && color2str != null && color1str != color2str)
  1563. {
  1564. Int32 color1;
  1565. Int32 color2;
  1566. try
  1567. {
  1568. color1 = Convert.ToInt32(color1str, 16);
  1569. }
  1570. // if the above throws ArgumentException, FormatException, or OverflowException, then abort
  1571. catch (Exception)
  1572. {
  1573. return sideToReplace;
  1574. }
  1575. try
  1576. {
  1577. color2 = Convert.ToInt32(color2str, 16);
  1578. }
  1579. // if the above throws ArgumentException, FormatException, or OverflowException, then abort
  1580. catch (Exception)
  1581. {
  1582. return inside1;
  1583. }
  1584. if (color1 < color2)
  1585. return inside1;
  1586. if (color2 < color1)
  1587. return sideToReplace;
  1588. return inside1;
  1589. }
  1590. return inside1;
  1591. }
  1592. private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tblStyleName)
  1593. {
  1594. var tblStyleChain = TableStyleStack(wDoc, tblStyleName)
  1595. .Reverse();
  1596. XElement rolledStyle = new XElement(W.style);
  1597. foreach (var style in tblStyleChain)
  1598. {
  1599. rolledStyle = MergeStyleElement(style, rolledStyle);
  1600. }
  1601. return rolledStyle;
  1602. }
  1603. private static XName[] SpecialCaseChildProperties =
  1604. {
  1605. W.tblPr,
  1606. W.trPr,
  1607. W.tcPr,
  1608. W.pPr,
  1609. W.rPr,
  1610. W.pBdr,
  1611. W.tabs,
  1612. W.rFonts,
  1613. W.ind,
  1614. W.spacing,
  1615. W.tblStylePr,
  1616. W.tcBorders,
  1617. W.tblBorders,
  1618. W.lang,
  1619. W.numPr,
  1620. };
  1621. private static XName[] MergeChildProperties =
  1622. {
  1623. W.tblPr,
  1624. W.trPr,
  1625. W.tcPr,
  1626. W.pPr,
  1627. W.rPr,
  1628. W.pBdr,
  1629. W.tcBorders,
  1630. W.tblBorders,
  1631. W.numPr,
  1632. };
  1633. private static string[] TableStyleOverrideTypes =
  1634. {
  1635. "band1Vert",
  1636. "band2Vert",
  1637. "band1Horz",
  1638. "band2Horz",
  1639. "firstCol",
  1640. "lastCol",
  1641. "firstRow",
  1642. "lastRow",
  1643. "neCell",
  1644. "nwCell",
  1645. "seCell",
  1646. "swCell",
  1647. };
  1648. private static Dictionary<string, XName> TableStyleOverrideXNameMap = new Dictionary<string, XName>
  1649. {
  1650. {"band1Vert", W.oddVBand},
  1651. {"band2Vert", W.evenVBand},
  1652. {"band1Horz", W.oddHBand},
  1653. {"band2Horz", W.evenHBand},
  1654. {"firstCol", W.firstColumn},
  1655. {"lastCol", W.lastColumn},
  1656. {"firstRow", W.firstRow},
  1657. {"lastRow", W.lastRow},
  1658. {"neCell", W.firstRowLastColumn},
  1659. {"nwCell", W.firstRowFirstColumn},
  1660. {"seCell", W.lastRowLastColumn},
  1661. {"swCell", W.lastRowFirstColumn},
  1662. };
  1663. private static Dictionary<XName, string> TableStyleOverrideXNameRevMap = new Dictionary<XName, string>
  1664. {
  1665. {W.oddVBand, "band1Vert"},
  1666. {W.evenVBand, "band2Vert"},
  1667. {W.oddHBand, "band1Horz"},
  1668. {W.evenHBand, "band2Horz"},
  1669. {W.firstColumn, "firstCol"},
  1670. {W.lastColumn, "lastCol"},
  1671. {W.firstRow, "firstRow"},
  1672. {W.lastRow, "lastRow"},
  1673. };
  1674. private static XElement MergeStyleElement(XElement higherPriorityElement, XElement lowerPriorityElement)
  1675. {
  1676. return MergeStyleElement(higherPriorityElement, lowerPriorityElement, null);
  1677. }
  1678. private static XElement MergeStyleElement(XElement higherPriorityElement, XElement lowerPriorityElement, bool? highPriIsDirectFormatting)
  1679. {
  1680. // If, when in the process of merging, the source element doesn't have a
  1681. // corresponding element in the merged element, then include the source element
  1682. // in the merged element.
  1683. if (lowerPriorityElement == null)
  1684. return higherPriorityElement;
  1685. if (higherPriorityElement == null)
  1686. return lowerPriorityElement;
  1687. var hpe = higherPriorityElement
  1688. .Elements()
  1689. .Where(e => !SpecialCaseChildProperties.Contains(e.Name))
  1690. .ToArray();
  1691. if (highPriIsDirectFormatting == true)
  1692. {
  1693. hpe = hpe
  1694. .Select(e =>
  1695. new XElement(e.Name,
  1696. e.Attributes(),
  1697. new XAttribute(PtOpenXml.pt + "fromDirect", true),
  1698. e.Elements()))
  1699. .ToArray();
  1700. }
  1701. var lpe = lowerPriorityElement
  1702. .Elements()
  1703. .Where(e => !SpecialCaseChildProperties.Contains(e.Name) && !hpe.Select(z => z.Name).Contains(e.Name))
  1704. .ToArray();
  1705. var ma = SpacingMerge(higherPriorityElement.Element(W.spacing), lowerPriorityElement.Element(W.spacing));
  1706. var rFonts = FontMerge(higherPriorityElement.Element(W.rFonts), lowerPriorityElement.Element(W.rFonts));
  1707. var tabs = TabsMerge(higherPriorityElement.Element(W.tabs), lowerPriorityElement.Element(W.tabs));
  1708. var ind = IndMerge(higherPriorityElement.Element(W.ind), lowerPriorityElement.Element(W.ind));
  1709. var lang = LangMerge(higherPriorityElement.Element(W.lang), lowerPriorityElement.Element(W.lang));
  1710. var mcp = MergeChildProperties
  1711. .Select(e =>
  1712. {
  1713. // test is here to prevent unnecessary recursion to make debugging easier
  1714. var h = higherPriorityElement.Element(e);
  1715. var l = lowerPriorityElement.Element(e);
  1716. if (h == null && l == null)
  1717. return null;
  1718. if (h == null && l != null)
  1719. return l;
  1720. if (h != null && l == null)
  1721. {
  1722. var newH = new XElement(h.Name,
  1723. h.Attributes(),
  1724. highPriIsDirectFormatting == true ? new XAttribute(PtOpenXml.pt + "fromDirect", true) : null,
  1725. h.Elements());
  1726. return newH;
  1727. }
  1728. return MergeStyleElement(h, l, highPriIsDirectFormatting);
  1729. })
  1730. .Where(m => m != null)
  1731. .ToArray();
  1732. var tsor = TableStyleOverrideTypes
  1733. .Select(e =>
  1734. {
  1735. // test is here to prevent unnecessary recursion to make debugging easier
  1736. var h = higherPriorityElement.Elements(W.tblStylePr).FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == e);
  1737. var l = lowerPriorityElement.Elements(W.tblStylePr).FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == e);
  1738. if (h == null && l == null)
  1739. return null;
  1740. if (h == null && l != null)
  1741. return l;
  1742. if (h != null && l == null)
  1743. return h;
  1744. return MergeStyleElement(h, l);
  1745. })
  1746. .Where(m => m != null)
  1747. .ToArray();
  1748. XElement newMergedElement = new XElement(higherPriorityElement.Name,
  1749. new XAttribute(XNamespace.Xmlns + "w", W.w),
  1750. higherPriorityElement.Attributes().Where(a => !a.IsNamespaceDeclaration),
  1751. hpe, // higher priority elements
  1752. lpe, // lower priority elements where there is not a higher priority element of same name
  1753. ind, // w:ind has very special rules
  1754. ma, // elements that require merged attributes
  1755. lang,
  1756. rFonts, // font merge is special case
  1757. tabs, // tabs merge is special case
  1758. mcp, // elements that need child properties to be merged
  1759. tsor // merged table style override elements
  1760. );
  1761. return newMergedElement;
  1762. }
  1763. private static XElement LangMerge(XElement hLang, XElement lLang)
  1764. {
  1765. if (hLang == null && lLang == null)
  1766. return null;
  1767. if (hLang != null && lLang == null)
  1768. return hLang;
  1769. if (lLang != null && hLang == null)
  1770. return lLang;
  1771. return new XElement(W.lang,
  1772. hLang.Attribute(W.val) != null ? hLang.Attribute(W.val) : lLang.Attribute(W.val),
  1773. hLang.Attribute(W.bidi) != null ? hLang.Attribute(W.bidi) : lLang.Attribute(W.bidi),
  1774. hLang.Attribute(W.eastAsia) != null ? hLang.Attribute(W.eastAsia) : lLang.Attribute(W.eastAsia));
  1775. }
  1776. private enum IndAttType
  1777. {
  1778. End,
  1779. FirstLineOrHanging,
  1780. Start,
  1781. Left,
  1782. Right,
  1783. None,
  1784. };
  1785. private static XElement IndMerge(XElement higherPriorityElement, XElement lowerPriorityElement)
  1786. {
  1787. if (higherPriorityElement == null && lowerPriorityElement == null)
  1788. return null;
  1789. if (higherPriorityElement != null && lowerPriorityElement == null)
  1790. return higherPriorityElement;
  1791. if (lowerPriorityElement != null && higherPriorityElement == null)
  1792. return lowerPriorityElement;
  1793. XElement hpe = new XElement(higherPriorityElement);
  1794. XElement lpe = new XElement(lowerPriorityElement);
  1795. if (hpe.Attribute(W.firstLine) != null)
  1796. lpe.Attributes(W.hanging).Remove();
  1797. if (hpe.Attribute(W.firstLineChars) != null)
  1798. lpe.Attributes(W.hangingChars).Remove();
  1799. if (hpe.Attribute(W.hanging) != null)
  1800. lpe.Attributes(W.firstLine).Remove();
  1801. if (hpe.Attribute(W.hangingChars) != null)
  1802. lpe.Attributes(W.firstLineChars).Remove();
  1803. var highPriAtts = hpe
  1804. .Attributes()
  1805. .Where(a => !a.IsNamespaceDeclaration)
  1806. .ToList();
  1807. var highPriAttNames = highPriAtts
  1808. .Select(a => a.Name);
  1809. var lowPriAtts = lpe
  1810. .Attributes()
  1811. .Where(a => !a.IsNamespaceDeclaration)
  1812. .Where(a => !highPriAttNames.Contains(a.Name))
  1813. .ToList();
  1814. var mergedElement = new XElement(higherPriorityElement.Name,
  1815. highPriAtts,
  1816. lowPriAtts);
  1817. return mergedElement;
  1818. }
  1819. // merge child tab elements
  1820. // they are additive, with the exception that if there are two elements at the same location,
  1821. // we need to take the higher, and not take the lower.
  1822. private static XElement TabsMerge(XElement higherPriorityElement, XElement lowerPriorityElement)
  1823. {
  1824. if (higherPriorityElement != null && lowerPriorityElement == null)
  1825. return higherPriorityElement;
  1826. if (higherPriorityElement == null && lowerPriorityElement != null)
  1827. return lowerPriorityElement;
  1828. if (higherPriorityElement == null && lowerPriorityElement == null)
  1829. return null;
  1830. var hps = higherPriorityElement.Elements().Select(e =>
  1831. new
  1832. {
  1833. Pos = WordprocessingMLUtil.StringToTwips((string)e.Attribute(W.pos)),
  1834. Pri = 1,
  1835. Element = e,
  1836. }
  1837. );
  1838. var lps = lowerPriorityElement.Elements().Select(e =>
  1839. new
  1840. {
  1841. Pos = WordprocessingMLUtil.StringToTwips((string)e.Attribute(W.pos)),
  1842. Pri = 2,
  1843. Element = e,
  1844. }
  1845. );
  1846. var newTabElements = hps.Concat(lps)
  1847. .GroupBy(s => s.Pos)
  1848. .Select(g => g.OrderBy(s => s.Pri).First().Element)
  1849. .Where(e => (string)e.Attribute(W.val) != "clear")
  1850. .OrderBy(e => WordprocessingMLUtil.StringToTwips((string)e.Attribute(W.pos)));
  1851. var newTabs = new XElement(W.tabs, newTabElements);
  1852. return newTabs;
  1853. }
  1854. private static XElement SpacingMerge(XElement hn, XElement ln)
  1855. {
  1856. if (hn == null && ln == null)
  1857. return null;
  1858. if (hn != null && ln == null)
  1859. return hn;
  1860. if (hn == null && ln != null)
  1861. return ln;
  1862. var mn1 = new XElement(W.spacing,
  1863. hn.Attributes(),
  1864. ln.Attributes().Where(a => hn.Attribute(a.Name) == null));
  1865. return mn1;
  1866. }
  1867. private static IEnumerable<XElement> TableStyleStack(WordprocessingDocument wDoc, string tblStyleName)
  1868. {
  1869. XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  1870. string currentStyle = tblStyleName;
  1871. while (true)
  1872. {
  1873. XElement style = sXDoc
  1874. .Root
  1875. .Elements(W.style).Where(s => (string)s.Attribute(W.type) == "table" &&
  1876. (string)s.Attribute(W.styleId) == currentStyle)
  1877. .FirstOrDefault();
  1878. if (style == null)
  1879. yield break;
  1880. yield return style;
  1881. currentStyle = (string)style.Elements(W.basedOn).Attributes(W.val).FirstOrDefault();
  1882. if (currentStyle == null)
  1883. yield break;
  1884. }
  1885. }
  1886. private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPriorityFont)
  1887. {
  1888. XElement rFonts;
  1889. if (higherPriorityFont == null)
  1890. return lowerPriorityFont;
  1891. if (lowerPriorityFont == null)
  1892. return higherPriorityFont;
  1893. if (higherPriorityFont == null && lowerPriorityFont == null)
  1894. return null;
  1895. rFonts = new XElement(W.rFonts,
  1896. (higherPriorityFont.Attribute(W.ascii) != null || higherPriorityFont.Attribute(W.asciiTheme) != null) ?
  1897. new[] { higherPriorityFont.Attribute(W.ascii), higherPriorityFont.Attribute(W.asciiTheme) } :
  1898. new[] { lowerPriorityFont.Attribute(W.ascii), lowerPriorityFont.Attribute(W.asciiTheme) },
  1899. (higherPriorityFont.Attribute(W.hAnsi) != null || higherPriorityFont.Attribute(W.hAnsiTheme) != null) ?
  1900. new[] { higherPriorityFont.Attribute(W.hAnsi), higherPriorityFont.Attribute(W.hAnsiTheme) } :
  1901. new[] { lowerPriorityFont.Attribute(W.hAnsi), lowerPriorityFont.Attribute(W.hAnsiTheme) },
  1902. (higherPriorityFont.Attribute(W.eastAsia) != null || higherPriorityFont.Attribute(W.eastAsiaTheme) != null) ?
  1903. new[] { higherPriorityFont.Attribute(W.eastAsia), higherPriorityFont.Attribute(W.eastAsiaTheme) } :
  1904. new[] { lowerPriorityFont.Attribute(W.eastAsia), lowerPriorityFont.Attribute(W.eastAsiaTheme) },
  1905. (higherPriorityFont.Attribute(W.cs) != null || higherPriorityFont.Attribute(W.cstheme) != null) ?
  1906. new[] { higherPriorityFont.Attribute(W.cs), higherPriorityFont.Attribute(W.cstheme) } :
  1907. new[] { lowerPriorityFont.Attribute(W.cs), lowerPriorityFont.Attribute(W.cstheme) },
  1908. (higherPriorityFont.Attribute(W.hint) != null ? higherPriorityFont.Attribute(W.hint) :
  1909. lowerPriorityFont.Attribute(W.hint))
  1910. );
  1911. return rFonts;
  1912. }
  1913. private static void AnnotateParagraphs(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement root, FormattingAssemblerSettings settings)
  1914. {
  1915. foreach (var para in root.Descendants(W.p))
  1916. {
  1917. AnnotateParagraph(fai, wDoc, para, settings);
  1918. }
  1919. }
  1920. private static void AnnotateParagraph(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement para, FormattingAssemblerSettings settings)
  1921. {
  1922. XElement localParaProps = para.Element(W.pPr);
  1923. if (localParaProps == null)
  1924. {
  1925. localParaProps = new XElement(W.pPr);
  1926. }
  1927. // get para table props, to be merged.
  1928. XElement tablepPr = null;
  1929. var blockLevelContentContainer = para
  1930. .Ancestors()
  1931. .FirstOrDefault(a => a.Name == W.body ||
  1932. a.Name == W.tbl ||
  1933. a.Name == W.txbxContent ||
  1934. a.Name == W.ftr ||
  1935. a.Name == W.hdr ||
  1936. a.Name == W.footnote ||
  1937. a.Name == W.endnote);
  1938. if (blockLevelContentContainer.Name == W.tbl)
  1939. {
  1940. XElement tbl = blockLevelContentContainer;
  1941. XElement style = tbl.Element(PtOpenXml.pt + "style");
  1942. XElement cellCnf = para.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault();
  1943. XElement rowCnf = para.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault();
  1944. if (style != null)
  1945. {
  1946. // roll up tblPr, trPr, and tcPr from within a specific style.
  1947. // add each of these to the table, in PowerTools namespace.
  1948. tablepPr = style.Element(W.pPr);
  1949. if (tablepPr == null)
  1950. tablepPr = new XElement(W.pPr);
  1951. foreach (var ot in TableStyleOverrideTypes)
  1952. {
  1953. XName attName = TableStyleOverrideXNameMap[ot];
  1954. if ((cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true) ||
  1955. (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true))
  1956. {
  1957. XElement o = style
  1958. .Elements(W.tblStylePr)
  1959. .Where(tsp => (string)tsp.Attribute(W.type) == ot)
  1960. .FirstOrDefault();
  1961. if (o != null)
  1962. {
  1963. XElement otpPr = o.Element(W.pPr);
  1964. tablepPr = MergeStyleElement(otpPr, tablepPr);
  1965. }
  1966. }
  1967. }
  1968. }
  1969. }
  1970. var stylesPart = wDoc.MainDocumentPart.StyleDefinitionsPart;
  1971. XDocument sXDoc = null;
  1972. if (stylesPart != null)
  1973. sXDoc = stylesPart.GetXDocument();
  1974. ListItemRetriever.ListItemInfo lif = para.Annotation<ListItemRetriever.ListItemInfo>();
  1975. XElement rolledParaProps = ParagraphStyleRollup(para, sXDoc, fai.DefaultParagraphStyleName);
  1976. if (lif != null && lif.IsZeroNumId)
  1977. rolledParaProps.Elements(W.ind).Remove();
  1978. XElement toggledParaProps = MergeStyleElement(rolledParaProps, tablepPr);
  1979. XElement mergedParaProps = MergeStyleElement(localParaProps, toggledParaProps);
  1980. string li = ListItemRetriever.RetrieveListItem(wDoc, para, settings.ListItemRetrieverSettings);
  1981. if (lif != null && lif.IsListItem)
  1982. {
  1983. if (settings.RestrictToSupportedNumberingFormats)
  1984. {
  1985. string numFmtForLevel = (string)lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.numFmt).Attributes(W.val).FirstOrDefault();
  1986. if (numFmtForLevel == null)
  1987. {
  1988. var numFmtElement = lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(MC.AlternateContent).Elements(MC.Choice).Elements(W.numFmt).FirstOrDefault();
  1989. if (numFmtElement != null && (string)numFmtElement.Attribute(W.val) == "custom")
  1990. numFmtForLevel = (string)numFmtElement.Attribute(W.format);
  1991. }
  1992. bool isLgl = lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.isLgl).Any();
  1993. if (isLgl && numFmtForLevel != "decimalZero")
  1994. numFmtForLevel = "decimal";
  1995. if (!AcceptableNumFormats.Contains(numFmtForLevel))
  1996. throw new UnsupportedNumberingFormatException(numFmtForLevel + " is not a supported numbering format");
  1997. }
  1998. int paragraphLevel = ListItemRetriever.GetParagraphLevel(para);
  1999. var numberingParaProps = lif
  2000. .Lvl(paragraphLevel)
  2001. .Elements(W.pPr)
  2002. .FirstOrDefault();
  2003. if (numberingParaProps == null)
  2004. {
  2005. numberingParaProps = new XElement(W.pPr);
  2006. }
  2007. else
  2008. {
  2009. numberingParaProps
  2010. .Elements()
  2011. .Where(e => e.Name != W.ind)
  2012. .Remove();
  2013. }
  2014. // have:
  2015. // - localParaProps
  2016. // - toggledParaProps
  2017. // - numberingParaProps
  2018. // if a paragraph contains a numPr with a numId=0, in other words, it is NOT a numbered item, then the indentation from the style
  2019. // hierarchy is ignored.
  2020. ListItemRetriever.ListItemInfo lii = para.Annotation<ListItemRetriever.ListItemInfo>();
  2021. if (lii.FromParagraph != null)
  2022. {
  2023. // order
  2024. // - toggledParaProps
  2025. // - numberingParaProps
  2026. // - localParaProps
  2027. mergedParaProps = MergeStyleElement(numberingParaProps, toggledParaProps);
  2028. mergedParaProps = MergeStyleElement(localParaProps, mergedParaProps);
  2029. }
  2030. else if (lii.FromStyle != null)
  2031. {
  2032. // order
  2033. // - numberingParaProps
  2034. // - toggledParaProps
  2035. // - localParaProps
  2036. mergedParaProps = MergeStyleElement(toggledParaProps, numberingParaProps);
  2037. mergedParaProps = MergeStyleElement(localParaProps, mergedParaProps);
  2038. }
  2039. }
  2040. else
  2041. {
  2042. mergedParaProps = MergeStyleElement(localParaProps, toggledParaProps);
  2043. }
  2044. // merge mergedParaProps with existing accumulatedParaProps, with mergedParaProps as high pri
  2045. // replace accumulatedParaProps with newly merged
  2046. XElement accumulatedParaProps = para.Element(PtOpenXml.pt + "pPr");
  2047. XElement newAccumulatedParaProps = MergeStyleElement(mergedParaProps, accumulatedParaProps);
  2048. AdjustFontAttributes(wDoc, para, newAccumulatedParaProps, newAccumulatedParaProps.Element(W.rPr), settings);
  2049. newAccumulatedParaProps.Name = PtOpenXml.pt + "pPr";
  2050. if (accumulatedParaProps != null)
  2051. {
  2052. accumulatedParaProps.ReplaceWith(newAccumulatedParaProps);
  2053. }
  2054. else
  2055. {
  2056. para.Add(newAccumulatedParaProps);
  2057. }
  2058. }
  2059. private static string[] AcceptableNumFormats = new[] {
  2060. "decimal",
  2061. "decimalZero",
  2062. "upperRoman",
  2063. "lowerRoman",
  2064. "upperLetter",
  2065. "lowerLetter",
  2066. "ordinal",
  2067. "cardinalText",
  2068. "ordinalText",
  2069. "bullet",
  2070. "0001, 0002, 0003, ...",
  2071. "none",
  2072. };
  2073. public static XElement ParagraphStyleRollup(XElement paragraph, XDocument stylesXDoc, string defaultParagraphStyleName)
  2074. {
  2075. var paraStyle = (string)paragraph
  2076. .Elements(W.pPr)
  2077. .Elements(W.pStyle)
  2078. .Attributes(W.val)
  2079. .FirstOrDefault();
  2080. if (paraStyle == null)
  2081. paraStyle = defaultParagraphStyleName;
  2082. var rolledUpParaStyleParaProps = new XElement(W.pPr);
  2083. if (stylesXDoc == null)
  2084. return rolledUpParaStyleParaProps;
  2085. if (paraStyle != null)
  2086. {
  2087. rolledUpParaStyleParaProps = ParaStyleParaPropsStack(stylesXDoc, paraStyle, paragraph)
  2088. .Reverse()
  2089. .Aggregate(new XElement(W.pPr),
  2090. (r, s) =>
  2091. {
  2092. var newParaProps = MergeStyleElement(s, r);
  2093. return newParaProps;
  2094. });
  2095. }
  2096. return rolledUpParaStyleParaProps;
  2097. }
  2098. private static IEnumerable<XElement> ParaStyleParaPropsStack(XDocument stylesXDoc, string paraStyleName, XElement para)
  2099. {
  2100. if (stylesXDoc == null)
  2101. yield break;
  2102. var localParaStyleName = paraStyleName;
  2103. while (localParaStyleName != null)
  2104. {
  2105. XElement paraStyle = stylesXDoc.Root.Elements(W.style).FirstOrDefault(s =>
  2106. s.Attribute(W.type).Value == "paragraph" &&
  2107. s.Attribute(W.styleId).Value == localParaStyleName);
  2108. if (paraStyle == null)
  2109. {
  2110. yield break;
  2111. }
  2112. if (paraStyle.Element(W.pPr) == null)
  2113. {
  2114. if (paraStyle.Element(W.rPr) != null)
  2115. {
  2116. var elementToYield2 = new XElement(W.pPr,
  2117. paraStyle.Element(W.rPr));
  2118. yield return elementToYield2;
  2119. }
  2120. localParaStyleName = (string)(paraStyle
  2121. .Elements(W.basedOn)
  2122. .Attributes(W.val)
  2123. .FirstOrDefault());
  2124. continue;
  2125. }
  2126. var elementToYield = new XElement(W.pPr,
  2127. paraStyle.Element(W.pPr).Attributes(),
  2128. paraStyle.Element(W.pPr).Elements(),
  2129. paraStyle.Element(W.rPr));
  2130. yield return (elementToYield);
  2131. var listItemInfo = para.Annotation<ListItemRetriever.ListItemInfo>();
  2132. if (listItemInfo != null)
  2133. {
  2134. if (listItemInfo.IsListItem)
  2135. {
  2136. XElement lipPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.pPr);
  2137. if (lipPr == null)
  2138. lipPr = new XElement(W.pPr);
  2139. XElement lirPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.rPr);
  2140. var elementToYield2 = new XElement(W.pPr,
  2141. lipPr.Attributes(),
  2142. lipPr.Elements(),
  2143. lirPr);
  2144. yield return (elementToYield2);
  2145. }
  2146. }
  2147. localParaStyleName = (string)paraStyle
  2148. .Elements(W.basedOn)
  2149. .Attributes(W.val)
  2150. .FirstOrDefault();
  2151. }
  2152. yield break;
  2153. }
  2154. private static void AnnotateRuns(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement root, FormattingAssemblerSettings settings)
  2155. {
  2156. var runsOrParas = root.Descendants()
  2157. .Where(rp =>
  2158. {
  2159. return rp.Name == W.r || rp.Name == W.p;
  2160. });
  2161. foreach (var runOrPara in runsOrParas)
  2162. {
  2163. AnnotateRunProperties(fai, wDoc, runOrPara, settings);
  2164. }
  2165. }
  2166. private static void AnnotateRunProperties(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement runOrPara, FormattingAssemblerSettings settings)
  2167. {
  2168. XElement localRunProps = null;
  2169. if (runOrPara.Name == W.p)
  2170. {
  2171. var rPr = runOrPara.Elements(W.pPr).Elements(W.rPr).FirstOrDefault();
  2172. if (rPr != null)
  2173. {
  2174. localRunProps = rPr;
  2175. }
  2176. }
  2177. else
  2178. {
  2179. localRunProps = runOrPara.Element(W.rPr);
  2180. }
  2181. if (localRunProps == null)
  2182. {
  2183. localRunProps = new XElement(W.rPr);
  2184. }
  2185. // get run table props, to be merged.
  2186. XElement tablerPr = null;
  2187. var blockLevelContentContainer = runOrPara
  2188. .Ancestors()
  2189. .FirstOrDefault(a => a.Name == W.body ||
  2190. a.Name == W.tbl ||
  2191. a.Name == W.txbxContent ||
  2192. a.Name == W.ftr ||
  2193. a.Name == W.hdr ||
  2194. a.Name == W.footnote ||
  2195. a.Name == W.endnote);
  2196. if (blockLevelContentContainer.Name == W.tbl)
  2197. {
  2198. XElement tbl = blockLevelContentContainer;
  2199. XElement style = tbl.Element(PtOpenXml.pt + "style");
  2200. XElement cellCnf = runOrPara.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault();
  2201. XElement rowCnf = runOrPara.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault();
  2202. if (style != null)
  2203. {
  2204. tablerPr = style.Element(W.rPr);
  2205. if (tablerPr == null)
  2206. tablerPr = new XElement(W.rPr);
  2207. foreach (var ot in TableStyleOverrideTypes)
  2208. {
  2209. XName attName = TableStyleOverrideXNameMap[ot];
  2210. if ((cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true) ||
  2211. (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true))
  2212. {
  2213. XElement o = style
  2214. .Elements(W.tblStylePr)
  2215. .Where(tsp => (string)tsp.Attribute(W.type) == ot)
  2216. .FirstOrDefault();
  2217. if (o != null)
  2218. {
  2219. XElement otrPr = o.Element(W.rPr);
  2220. tablerPr = MergeStyleElement(otrPr, tablerPr);
  2221. }
  2222. }
  2223. }
  2224. }
  2225. }
  2226. XElement rolledRunProps = CharStyleRollup(fai, wDoc, runOrPara);
  2227. var toggledRunProps = ToggleMergeRunProps(rolledRunProps, tablerPr);
  2228. var currentRunProps = runOrPara.Element(PtOpenXml.rPr); // this is already stored on the run from previous aggregation of props
  2229. var mergedRunProps = MergeStyleElement(toggledRunProps, currentRunProps);
  2230. var newMergedRunProps = MergeStyleElement(localRunProps, mergedRunProps);
  2231. XElement pPr = null;
  2232. if (runOrPara.Name == W.p)
  2233. pPr = runOrPara.Element(PtOpenXml.pPr);
  2234. AdjustFontAttributes(wDoc, runOrPara, pPr, newMergedRunProps, settings);
  2235. newMergedRunProps.Name = PtOpenXml.rPr;
  2236. if (currentRunProps != null)
  2237. {
  2238. currentRunProps.ReplaceWith(newMergedRunProps);
  2239. }
  2240. else
  2241. {
  2242. runOrPara.Add(newMergedRunProps);
  2243. }
  2244. }
  2245. private static XElement CharStyleRollup(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement runOrPara)
  2246. {
  2247. var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  2248. string charStyle = null;
  2249. string paraStyle = null;
  2250. XElement rPr = null;
  2251. XElement pPr = null;
  2252. XElement pStyle = null;
  2253. XElement rStyle = null;
  2254. CachedParaInfo cpi = null; // CachedParaInfo is an optimization for the case where a paragraph contains thousands of runs.
  2255. if (runOrPara.Name == W.p)
  2256. {
  2257. cpi = runOrPara.Annotation<CachedParaInfo>();
  2258. if (cpi != null)
  2259. pPr = cpi.ParagraphProperties;
  2260. else
  2261. {
  2262. pPr = runOrPara.Element(W.pPr);
  2263. if (pPr != null)
  2264. {
  2265. paraStyle = (string)pPr.Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
  2266. }
  2267. else
  2268. {
  2269. paraStyle = fai.DefaultParagraphStyleName;
  2270. }
  2271. cpi = new CachedParaInfo
  2272. {
  2273. ParagraphProperties = pPr,
  2274. ParagraphStyleName = paraStyle,
  2275. };
  2276. runOrPara.AddAnnotation(cpi);
  2277. }
  2278. if (pPr != null)
  2279. {
  2280. rPr = pPr.Element(W.rPr);
  2281. }
  2282. }
  2283. else
  2284. {
  2285. rPr = runOrPara.Element(W.rPr);
  2286. }
  2287. if (rPr != null)
  2288. {
  2289. rStyle = rPr.Element(W.rStyle);
  2290. if (rStyle != null)
  2291. {
  2292. charStyle = (string)rStyle.Attribute(W.val);
  2293. }
  2294. else
  2295. {
  2296. if (runOrPara.Name == W.r)
  2297. charStyle = (string)runOrPara
  2298. .Ancestors(W.p)
  2299. .Take(1)
  2300. .Elements(W.pPr)
  2301. .Elements(W.pStyle)
  2302. .Attributes(W.val)
  2303. .FirstOrDefault();
  2304. else
  2305. charStyle = (string)runOrPara
  2306. .Elements(W.pPr)
  2307. .Elements(W.pStyle)
  2308. .Attributes(W.val)
  2309. .FirstOrDefault();
  2310. }
  2311. }
  2312. if (charStyle == null)
  2313. {
  2314. if (runOrPara.Name == W.r)
  2315. {
  2316. var ancestorPara = runOrPara.Ancestors(W.p).First();
  2317. cpi = ancestorPara.Annotation<CachedParaInfo>();
  2318. if (cpi != null)
  2319. charStyle = cpi.ParagraphStyleName;
  2320. else
  2321. charStyle = (string)runOrPara.Ancestors(W.p).First().Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
  2322. }
  2323. if (charStyle == null)
  2324. {
  2325. charStyle = fai.DefaultParagraphStyleName;
  2326. }
  2327. }
  2328. // A run always must have an ancestor paragraph.
  2329. XElement para = null;
  2330. var rolledUpParaStyleRunProps = new XElement(W.rPr);
  2331. if (runOrPara.Name == W.r)
  2332. {
  2333. para = runOrPara.Ancestors(W.p).FirstOrDefault();
  2334. }
  2335. else
  2336. {
  2337. para = runOrPara;
  2338. }
  2339. cpi = para.Annotation<CachedParaInfo>();
  2340. if (cpi != null)
  2341. {
  2342. pPr = cpi.ParagraphProperties;
  2343. }
  2344. else
  2345. {
  2346. pPr = para.Element(W.pPr);
  2347. }
  2348. if (pPr != null)
  2349. {
  2350. pStyle = pPr.Element(W.pStyle);
  2351. if (pStyle != null)
  2352. {
  2353. paraStyle = (string)pStyle.Attribute(W.val);
  2354. }
  2355. else
  2356. {
  2357. paraStyle = fai.DefaultParagraphStyleName;
  2358. }
  2359. }
  2360. else
  2361. paraStyle = fai.DefaultParagraphStyleName;
  2362. string key = (paraStyle == null ? "[null]" : paraStyle) + "~|~" +
  2363. (charStyle == null ? "[null]" : charStyle);
  2364. XElement rolledRunProps = null;
  2365. if (fai.RolledCharacterStyles.ContainsKey(key))
  2366. rolledRunProps = fai.RolledCharacterStyles[key];
  2367. else
  2368. {
  2369. XElement rolledUpCharStyleRunProps = new XElement(W.rPr);
  2370. if (charStyle != null)
  2371. {
  2372. rolledUpCharStyleRunProps =
  2373. CharStyleStack(wDoc, charStyle)
  2374. .Aggregate(new XElement(W.rPr),
  2375. (r, s) =>
  2376. {
  2377. var newRunProps = MergeStyleElement(s, r);
  2378. return newRunProps;
  2379. });
  2380. }
  2381. if (paraStyle != null)
  2382. {
  2383. rolledUpParaStyleRunProps = ParaStyleRunPropsStack(wDoc, paraStyle)
  2384. .Aggregate(new XElement(W.rPr),
  2385. (r, s) =>
  2386. {
  2387. var newCharStyleRunProps = MergeStyleElement(s, r);
  2388. return newCharStyleRunProps;
  2389. });
  2390. }
  2391. rolledRunProps = MergeStyleElement(rolledUpCharStyleRunProps, rolledUpParaStyleRunProps);
  2392. fai.RolledCharacterStyles.Add(key, rolledRunProps);
  2393. }
  2394. return rolledRunProps;
  2395. }
  2396. private static IEnumerable<XElement> ParaStyleRunPropsStack(WordprocessingDocument wDoc, string paraStyleName)
  2397. {
  2398. var localParaStyleName = paraStyleName;
  2399. var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  2400. var rValue = new Stack<XElement>();
  2401. while (localParaStyleName != null)
  2402. {
  2403. var paraStyle = sXDoc.Root.Elements(W.style).FirstOrDefault(s =>
  2404. {
  2405. return (string)s.Attribute(W.type) == "paragraph" &&
  2406. (string)s.Attribute(W.styleId) == localParaStyleName;
  2407. });
  2408. if (paraStyle == null)
  2409. {
  2410. return rValue;
  2411. }
  2412. if (paraStyle.Element(W.rPr) != null)
  2413. {
  2414. rValue.Push(paraStyle.Element(W.rPr));
  2415. }
  2416. localParaStyleName = (string)paraStyle
  2417. .Elements(W.basedOn)
  2418. .Attributes(W.val)
  2419. .FirstOrDefault();
  2420. }
  2421. return rValue;
  2422. }
  2423. // returns collection of run properties
  2424. private static IEnumerable<XElement> CharStyleStack(WordprocessingDocument wDoc, string charStyleName)
  2425. {
  2426. var localCharStyleName = charStyleName;
  2427. var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
  2428. var rValue = new Stack<XElement>();
  2429. while (localCharStyleName != null)
  2430. {
  2431. XElement basedOn = null;
  2432. // first look for character style
  2433. var charStyle = sXDoc.Root.Elements(W.style).FirstOrDefault(s =>
  2434. {
  2435. return (string)s.Attribute(W.type) == "character" &&
  2436. (string)s.Attribute(W.styleId) == localCharStyleName;
  2437. });
  2438. // if not found, look for paragraph style
  2439. if (charStyle == null)
  2440. {
  2441. charStyle = sXDoc.Root.Elements(W.style).FirstOrDefault(s =>
  2442. {
  2443. return (string)s.Attribute(W.styleId) == localCharStyleName;
  2444. });
  2445. }
  2446. if (charStyle == null)
  2447. {
  2448. return rValue;
  2449. }
  2450. if (charStyle.Element(W.rPr) == null)
  2451. {
  2452. basedOn = charStyle.Element(W.basedOn);
  2453. if (basedOn != null)
  2454. {
  2455. localCharStyleName = (string)basedOn.Attribute(W.val);
  2456. }
  2457. else
  2458. {
  2459. return rValue;
  2460. }
  2461. }
  2462. rValue.Push(charStyle.Element(W.rPr));
  2463. localCharStyleName = null;
  2464. basedOn = charStyle.Element(W.basedOn);
  2465. if (basedOn != null)
  2466. {
  2467. localCharStyleName = (string)basedOn.Attribute(W.val);
  2468. }
  2469. }
  2470. return rValue;
  2471. }
  2472. private static XElement ToggleMergeRunProps(XElement higherPriorityElement, XElement lowerPriorityElement)
  2473. {
  2474. if (lowerPriorityElement == null)
  2475. return higherPriorityElement;
  2476. if (higherPriorityElement == null)
  2477. return lowerPriorityElement;
  2478. var hpe = higherPriorityElement.Elements().Select(e => e.Name).ToArray();
  2479. var newMergedElement = new XElement(higherPriorityElement.Name,
  2480. higherPriorityElement.Attributes(),
  2481. // process toggle properties
  2482. higherPriorityElement.Elements()
  2483. .Where(e => { return e.Name != W.rFonts; })
  2484. .Select(higherChildElement =>
  2485. {
  2486. if (TogglePropertyNames.Contains(higherChildElement.Name))
  2487. {
  2488. var lowerChildElement = lowerPriorityElement.Element(higherChildElement.Name);
  2489. if (lowerChildElement == null)
  2490. {
  2491. return higherChildElement;
  2492. }
  2493. var bHigher = higherChildElement.Attribute(W.val) == null || higherChildElement.Attribute(W.val).ToBoolean() == true;
  2494. var bLower = lowerChildElement.Attribute(W.val) == null || lowerChildElement.Attribute(W.val).ToBoolean() == true;
  2495. // if higher is true and lower is false, then return true element
  2496. if (bHigher && !bLower)
  2497. {
  2498. return higherChildElement;
  2499. }
  2500. // if higher is false and lower is true, then return false element
  2501. if (!bHigher && bLower)
  2502. {
  2503. return higherChildElement;
  2504. }
  2505. // if higher and lower are both true, then return false
  2506. if (bHigher && bLower)
  2507. {
  2508. return new XElement(higherChildElement.Name,
  2509. new XAttribute(W.val, "0"));
  2510. }
  2511. // otherwise, both higher and lower are false so can return higher element.
  2512. return higherChildElement;
  2513. }
  2514. return higherChildElement;
  2515. }),
  2516. FontMerge(higherPriorityElement.Element(W.rFonts), lowerPriorityElement.Element(W.rFonts)),
  2517. // take lower priority elements where there is not a higher priority element of same name
  2518. lowerPriorityElement.Elements()
  2519. .Where(e =>
  2520. {
  2521. return e.Name != W.rFonts && !hpe.Contains(e.Name);
  2522. }));
  2523. return newMergedElement;
  2524. }
  2525. private static XName[] TogglePropertyNames = new[] {
  2526. W.b,
  2527. W.bCs,
  2528. W.caps,
  2529. W.emboss,
  2530. W.i,
  2531. W.iCs,
  2532. W.imprint,
  2533. W.outline,
  2534. W.shadow,
  2535. W.smallCaps,
  2536. W.strike,
  2537. W.vanish
  2538. };
  2539. private static XName[] PropertyNames = new[] {
  2540. W.cs,
  2541. W.rtl,
  2542. W.u,
  2543. W.color,
  2544. W.highlight,
  2545. W.shd
  2546. };
  2547. public class CharStyleAttributes
  2548. {
  2549. public string AsciiFont;
  2550. public string HAnsiFont;
  2551. public string EastAsiaFont;
  2552. public string CsFont;
  2553. public string Hint;
  2554. public bool Rtl;
  2555. public string LatinLang;
  2556. public string BidiLang;
  2557. public string EastAsiaLang;
  2558. public Dictionary<XName, bool?> ToggleProperties;
  2559. public Dictionary<XName, XElement> Properties;
  2560. public CharStyleAttributes(XElement pPr, XElement rPr)
  2561. {
  2562. ToggleProperties = new Dictionary<XName, bool?>();
  2563. Properties = new Dictionary<XName, XElement>();
  2564. if (rPr == null)
  2565. return;
  2566. foreach (XName xn in TogglePropertyNames)
  2567. {
  2568. ToggleProperties[xn] = GetBoolProperty(rPr, xn);
  2569. }
  2570. foreach (XName xn in PropertyNames)
  2571. {
  2572. Properties[xn] = GetXmlProperty(rPr, xn);
  2573. }
  2574. var rFonts = rPr.Element(W.rFonts);
  2575. if (rFonts == null)
  2576. {
  2577. this.AsciiFont = null;
  2578. this.HAnsiFont = null;
  2579. this.EastAsiaFont = null;
  2580. this.CsFont = null;
  2581. this.Hint = null;
  2582. }
  2583. else
  2584. {
  2585. this.AsciiFont = (string)(rFonts.Attribute(W.ascii));
  2586. this.HAnsiFont = (string)(rFonts.Attribute(W.hAnsi));
  2587. this.EastAsiaFont = (string)(rFonts.Attribute(W.eastAsia));
  2588. this.CsFont = (string)(rFonts.Attribute(W.cs));
  2589. this.Hint = (string)(rFonts.Attribute(W.hint));
  2590. }
  2591. XElement csel = this.Properties[W.cs];
  2592. bool cs = csel != null && (csel.Attribute(W.val) == null || csel.Attribute(W.val).ToBoolean() == true);
  2593. XElement rtlel = this.Properties[W.rtl];
  2594. bool rtl = rtlel != null && (rtlel.Attribute(W.val) == null || rtlel.Attribute(W.val).ToBoolean() == true);
  2595. var bidi = false;
  2596. if (pPr != null)
  2597. {
  2598. XElement bidiel = pPr.Element(W.bidi);
  2599. bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true);
  2600. }
  2601. Rtl = cs || rtl || bidi;
  2602. var lang = rPr.Element(W.lang);
  2603. if (lang != null)
  2604. {
  2605. LatinLang = (string)lang.Attribute(W.val);
  2606. BidiLang = (string)lang.Attribute(W.bidi);
  2607. EastAsiaLang = (string)lang.Attribute(W.eastAsia);
  2608. }
  2609. }
  2610. private static bool? GetBoolProperty(XElement rPr, XName propertyName)
  2611. {
  2612. if (rPr.Element(propertyName) == null)
  2613. return null;
  2614. var s = (string)rPr.Element(propertyName).Attribute(W.val);
  2615. if (s == null)
  2616. return true;
  2617. if (s == "1")
  2618. return true;
  2619. if (s == "0")
  2620. return false;
  2621. if (s == "true")
  2622. return true;
  2623. if (s == "false")
  2624. return false;
  2625. if (s == "on")
  2626. return true;
  2627. if (s == "off")
  2628. return false;
  2629. return (bool)(rPr.Element(propertyName).Attribute(W.val));
  2630. }
  2631. private static XElement GetXmlProperty(XElement rPr, XName propertyName)
  2632. {
  2633. return rPr.Element(propertyName);
  2634. }
  2635. private static XName[] TogglePropertyNames = new[] {
  2636. W.b,
  2637. W.bCs,
  2638. W.caps,
  2639. W.emboss,
  2640. W.i,
  2641. W.iCs,
  2642. W.imprint,
  2643. W.outline,
  2644. W.shadow,
  2645. W.smallCaps,
  2646. W.strike,
  2647. W.vanish
  2648. };
  2649. private static XName[] PropertyNames = new[] {
  2650. W.cs,
  2651. W.rtl,
  2652. W.u,
  2653. W.color,
  2654. W.highlight,
  2655. W.shd
  2656. };
  2657. }
  2658. private static HashSet<char> WeakAndNeutralDirectionalCharacters = new HashSet<char>() {
  2659. '0',
  2660. '1',
  2661. '2',
  2662. '3',
  2663. '4',
  2664. '5',
  2665. '6',
  2666. '7',
  2667. '8',
  2668. '9',
  2669. '+',
  2670. '-',
  2671. ':',
  2672. ',',
  2673. '.',
  2674. '|',
  2675. '\t',
  2676. '\r',
  2677. '\n',
  2678. ' ',
  2679. '\x00A0', // non breaking space
  2680. '\x00B0', // degree sign
  2681. '\x066B', // arabic decimal separator
  2682. '\x066C', // arabic thousands separator
  2683. '\x0627', // arabic pipe
  2684. '\x20A0', // start currency symbols
  2685. '\x20A1',
  2686. '\x20A2',
  2687. '\x20A3',
  2688. '\x20A4',
  2689. '\x20A5',
  2690. '\x20A6',
  2691. '\x20A7',
  2692. '\x20A8',
  2693. '\x20A9',
  2694. '\x20AA',
  2695. '\x20AB',
  2696. '\x20AC',
  2697. '\x20AD',
  2698. '\x20AE',
  2699. '\x20AF',
  2700. '\x20B0',
  2701. '\x20B1',
  2702. '\x20B2',
  2703. '\x20B3',
  2704. '\x20B4',
  2705. '\x20B5',
  2706. '\x20B6',
  2707. '\x20B7',
  2708. '\x20B8',
  2709. '\x20B9',
  2710. '\x20BA',
  2711. '\x20BB',
  2712. '\x20BC',
  2713. '\x20BD',
  2714. '\x20BE',
  2715. '\x20BF',
  2716. '\x20C0',
  2717. '\x20C1',
  2718. '\x20C2',
  2719. '\x20C3',
  2720. '\x20C4',
  2721. '\x20C5',
  2722. '\x20C6',
  2723. '\x20C7',
  2724. '\x20C8',
  2725. '\x20C9',
  2726. '\x20CA',
  2727. '\x20CB',
  2728. '\x20CC',
  2729. '\x20CD',
  2730. '\x20CE',
  2731. '\x20CF', // end currency symbols
  2732. '\x0660', // "Arabic" Indic Numeral Forms Iraq and West
  2733. '\x0661',
  2734. '\x0662',
  2735. '\x0663',
  2736. '\x0664',
  2737. '\x0665',
  2738. '\x0666',
  2739. '\x0667',
  2740. '\x0668',
  2741. '\x0669',
  2742. '\x06F0', // "Arabic" Indic Numberal Forms Iran and East
  2743. '\x06F1',
  2744. '\x06F2',
  2745. '\x06F3',
  2746. '\x06F4',
  2747. '\x06F5',
  2748. '\x06F6',
  2749. '\x06F7',
  2750. '\x06F8',
  2751. '\x06F9',
  2752. };
  2753. private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement paraOrRun, XElement pPr,
  2754. XElement rPr, FormattingAssemblerSettings settings)
  2755. {
  2756. XDocument themeXDoc = null;
  2757. if (wDoc.MainDocumentPart.ThemePart != null)
  2758. themeXDoc = wDoc.MainDocumentPart.ThemePart.GetXDocument();
  2759. XElement fontScheme = null;
  2760. XElement majorFont = null;
  2761. XElement minorFont = null;
  2762. if (themeXDoc != null)
  2763. {
  2764. fontScheme = themeXDoc.Root.Element(A.themeElements).Element(A.fontScheme);
  2765. majorFont = fontScheme.Element(A.majorFont);
  2766. minorFont = fontScheme.Element(A.minorFont);
  2767. }
  2768. var rFonts = rPr.Element(W.rFonts);
  2769. if (rFonts == null)
  2770. {
  2771. return;
  2772. }
  2773. var asciiTheme = (string)rFonts.Attribute(W.asciiTheme);
  2774. var hAnsiTheme = (string)rFonts.Attribute(W.hAnsiTheme);
  2775. var eastAsiaTheme = (string)rFonts.Attribute(W.eastAsiaTheme);
  2776. var cstheme = (string)rFonts.Attribute(W.cstheme);
  2777. string ascii = null;
  2778. string hAnsi = null;
  2779. string eastAsia = null;
  2780. string cs = null;
  2781. XElement minorLatin = null;
  2782. string minorLatinTypeface = null;
  2783. XElement majorLatin = null;
  2784. string majorLatinTypeface = null;
  2785. if (minorFont != null)
  2786. {
  2787. minorLatin = minorFont.Element(A.latin);
  2788. minorLatinTypeface = (string)minorLatin.Attribute("typeface");
  2789. }
  2790. if (majorFont != null)
  2791. {
  2792. majorLatin = majorFont.Element(A.latin);
  2793. majorLatinTypeface = (string)majorLatin.Attribute("typeface");
  2794. }
  2795. if (asciiTheme != null)
  2796. {
  2797. if (asciiTheme.StartsWith("minor") && minorLatinTypeface != null)
  2798. {
  2799. ascii = minorLatinTypeface;
  2800. }
  2801. else if (asciiTheme.StartsWith("major") && majorLatinTypeface != null)
  2802. {
  2803. ascii = majorLatinTypeface;
  2804. }
  2805. }
  2806. if (hAnsiTheme != null)
  2807. {
  2808. if (hAnsiTheme.StartsWith("minor") && minorLatinTypeface != null)
  2809. {
  2810. hAnsi = minorLatinTypeface;
  2811. }
  2812. else if (hAnsiTheme.StartsWith("major") && majorLatinTypeface != null)
  2813. {
  2814. hAnsi = majorLatinTypeface;
  2815. }
  2816. }
  2817. if (eastAsiaTheme != null)
  2818. {
  2819. if (eastAsiaTheme.StartsWith("minor") && minorLatinTypeface != null)
  2820. {
  2821. eastAsia = minorLatinTypeface;
  2822. }
  2823. else if (eastAsiaTheme.StartsWith("major") && majorLatinTypeface != null)
  2824. {
  2825. eastAsia = majorLatinTypeface;
  2826. }
  2827. }
  2828. if (cstheme != null)
  2829. {
  2830. if (cstheme.StartsWith("minor") && minorFont != null)
  2831. {
  2832. cs = (string)minorFont.Element(A.cs).Attribute("typeface");
  2833. }
  2834. else if (cstheme.StartsWith("major") && majorFont != null)
  2835. {
  2836. cs = (string)majorFont.Element(A.cs).Attribute("typeface");
  2837. }
  2838. }
  2839. if (ascii != null)
  2840. {
  2841. rFonts.SetAttributeValue(W.ascii, ascii);
  2842. }
  2843. if (hAnsi != null)
  2844. {
  2845. rFonts.SetAttributeValue(W.hAnsi, hAnsi);
  2846. }
  2847. if (eastAsia != null)
  2848. {
  2849. rFonts.SetAttributeValue(W.eastAsia, eastAsia);
  2850. }
  2851. if (cs != null)
  2852. {
  2853. rFonts.SetAttributeValue(W.cs, cs);
  2854. }
  2855. var firstTextNode = paraOrRun.Descendants(W.t).FirstOrDefault(t => t.Value.Length > 0);
  2856. string str = " ";
  2857. // if there is a run with no text in it, then no need to do any of the rest of this method.
  2858. if (firstTextNode == null && paraOrRun.Name == W.r)
  2859. return;
  2860. if (firstTextNode != null)
  2861. str = firstTextNode.Value;
  2862. var csa = new CharStyleAttributes(pPr, rPr);
  2863. // This module determines the font based on just the first character.
  2864. // Technically, a run can contain characters from different Unicode code blocks, and hence should be rendered with different fonts.
  2865. // However, Word breaks up runs that use more than one font into multiple runs. Other producers of WordprocessingML may not, so in
  2866. // that case, this routine may need to be augmented to look at all characters in a run.
  2867. /*
  2868. old code
  2869. var fontFamilies = str.select(function (c) {
  2870. var ft = Pav.DetermineFontTypeFromCharacter(c, csa);
  2871. switch (ft) {
  2872. case Pav.FontType.Ascii:
  2873. return cast(rFonts.attribute(W.ascii));
  2874. case Pav.FontType.HAnsi:
  2875. return cast(rFonts.attribute(W.hAnsi));
  2876. case Pav.FontType.EastAsia:
  2877. return cast(rFonts.attribute(W.eastAsia));
  2878. case Pav.FontType.CS:
  2879. return cast(rFonts.attribute(W.cs));
  2880. default:
  2881. return null;
  2882. }
  2883. })
  2884. .where(function (f) { return f != null && f != ""; })
  2885. .distinct()
  2886. .select(function (f) { return new Pav.FontFamily(f); })
  2887. .toArray();
  2888. */
  2889. var charToExamine = str.FirstOrDefault(c => ! WeakAndNeutralDirectionalCharacters.Contains(c));
  2890. if (charToExamine == '\0')
  2891. charToExamine = str[0];
  2892. var ft = DetermineFontTypeFromCharacter(charToExamine, csa);
  2893. string fontType = null;
  2894. string languageType = null;
  2895. switch (ft)
  2896. {
  2897. case FontType.Ascii:
  2898. fontType = (string)rFonts.Attribute(W.ascii);
  2899. languageType = "western";
  2900. break;
  2901. case FontType.HAnsi:
  2902. fontType = (string)rFonts.Attribute(W.hAnsi);
  2903. languageType = "western";
  2904. break;
  2905. case FontType.EastAsia:
  2906. if (settings.RestrictToSupportedLanguages)
  2907. throw new UnsupportedLanguageException("EastAsia languages are not supported");
  2908. fontType = (string)rFonts.Attribute(W.eastAsia);
  2909. languageType = "eastAsia";
  2910. break;
  2911. case FontType.CS:
  2912. if (settings.RestrictToSupportedLanguages)
  2913. throw new UnsupportedLanguageException("Complex script (RTL) languages are not supported");
  2914. fontType = (string)rFonts.Attribute(W.cs);
  2915. languageType = "bidi";
  2916. break;
  2917. }
  2918. if (fontType != null)
  2919. {
  2920. if (paraOrRun.Attribute(PtOpenXml.FontName) == null)
  2921. {
  2922. XAttribute fta = new XAttribute(PtOpenXml.FontName, fontType.ToString());
  2923. paraOrRun.Add(fta);
  2924. }
  2925. else
  2926. {
  2927. paraOrRun.Attribute(PtOpenXml.FontName).Value = fontType.ToString();
  2928. }
  2929. }
  2930. if (languageType != null)
  2931. {
  2932. if (paraOrRun.Attribute(PtOpenXml.LanguageType) == null)
  2933. {
  2934. XAttribute lta = new XAttribute(PtOpenXml.LanguageType, languageType);
  2935. paraOrRun.Add(lta);
  2936. }
  2937. else
  2938. {
  2939. paraOrRun.Attribute(PtOpenXml.LanguageType).Value = languageType;
  2940. }
  2941. }
  2942. }
  2943. public enum FontType
  2944. {
  2945. Ascii,
  2946. HAnsi,
  2947. EastAsia,
  2948. CS
  2949. };
  2950. // The algorithm for this method comes from the implementer notes in [MS-OI29500].pdf
  2951. // section 2.1.87
  2952. // The implementer notes are at:
  2953. // http://msdn.microsoft.com/en-us/library/ee908652.aspx
  2954. public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttributes csa)
  2955. {
  2956. // If the run has the cs element ("[ISO/IEC-29500-1] §17.3.2.7; cs") or the rtl element ("[ISO/IEC-29500-1] §17.3.2.30; rtl"),
  2957. // then the cs (or cstheme if defined) font is used, regardless of the Unicode character values of the run’s content.
  2958. if (csa.Rtl)
  2959. {
  2960. return FontType.CS;
  2961. }
  2962. // A large percentage of characters will fall in the following rule.
  2963. // Unicode Block: Basic Latin
  2964. if (ch >= 0x00 && ch <= 0x7f)
  2965. {
  2966. return FontType.Ascii;
  2967. }
  2968. // If the eastAsia (or eastAsiaTheme if defined) attribute’s value is “Times New Roman” and the ascii (or asciiTheme if defined)
  2969. // and hAnsi (or hAnsiTheme if defined) attributes are equal, then the ascii (or asciiTheme if defined) font is used.
  2970. if (csa.EastAsiaFont == "Times New Roman" &&
  2971. csa.AsciiFont == csa.HAnsiFont)
  2972. {
  2973. return FontType.Ascii;
  2974. }
  2975. // Unicode BLock: Latin-1 Supplement
  2976. if (ch >= 0xA0 && ch <= 0xFF)
  2977. {
  2978. if (csa.Hint == "eastAsia")
  2979. {
  2980. if (ch == 0xA1 ||
  2981. ch == 0xA4 ||
  2982. ch == 0xA7 ||
  2983. ch == 0xA8 ||
  2984. ch == 0xAA ||
  2985. ch == 0xAD ||
  2986. ch == 0xAF ||
  2987. (ch >= 0xB0 && ch <= 0xB4) ||
  2988. (ch >= 0xB6 && ch <= 0xBA) ||
  2989. (ch >= 0xBC && ch <= 0xBF) ||
  2990. ch == 0xD7 ||
  2991. ch == 0xF7)
  2992. {
  2993. return FontType.EastAsia;
  2994. }
  2995. if (csa.EastAsiaLang == "zh-hant" ||
  2996. csa.EastAsiaLang == "zh-hans")
  2997. {
  2998. if (ch == 0xE0 ||
  2999. ch == 0xE1 ||
  3000. (ch >= 0xE8 && ch <= 0xEA) ||
  3001. (ch >= 0xEC && ch <= 0xED) ||
  3002. (ch >= 0xF2 && ch <= 0xF3) ||
  3003. (ch >= 0xF9 && ch <= 0xFA) ||
  3004. ch == 0xFC)
  3005. {
  3006. return FontType.EastAsia;
  3007. }
  3008. }
  3009. }
  3010. return FontType.HAnsi;
  3011. }
  3012. // Unicode Block: Latin Extended-A
  3013. if (ch >= 0x0100 && ch <= 0x017F)
  3014. {
  3015. if (csa.Hint == "eastAsia")
  3016. {
  3017. if (csa.EastAsiaLang == "zh-hant" ||
  3018. csa.EastAsiaLang == "zh-hans"
  3019. /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */)
  3020. {
  3021. return FontType.EastAsia;
  3022. }
  3023. }
  3024. return FontType.HAnsi;
  3025. }
  3026. // Unicode Block: Latin Extended-B
  3027. if (ch >= 0x0180 && ch <= 0x024F)
  3028. {
  3029. if (csa.Hint == "eastAsia")
  3030. {
  3031. if (csa.EastAsiaLang == "zh-hant" ||
  3032. csa.EastAsiaLang == "zh-hans"
  3033. /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */)
  3034. {
  3035. return FontType.EastAsia;
  3036. }
  3037. }
  3038. return FontType.HAnsi;
  3039. }
  3040. // Unicode Block: IPA Extensions
  3041. if (ch >= 0x0250 && ch <= 0x02AF)
  3042. {
  3043. if (csa.Hint == "eastAsia")
  3044. {
  3045. if (csa.EastAsiaLang == "zh-hant" ||
  3046. csa.EastAsiaLang == "zh-hans"
  3047. /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */)
  3048. {
  3049. return FontType.EastAsia;
  3050. }
  3051. }
  3052. return FontType.HAnsi;
  3053. }
  3054. // Unicode Block: Spacing Modifier Letters
  3055. if (ch >= 0x02B0 && ch <= 0x02FF)
  3056. {
  3057. if (csa.Hint == "eastAsia")
  3058. {
  3059. return FontType.EastAsia;
  3060. }
  3061. return FontType.HAnsi;
  3062. }
  3063. // Unicode Block: Combining Diacritic Marks
  3064. if (ch >= 0x0300 && ch <= 0x036F)
  3065. {
  3066. if (csa.Hint == "eastAsia")
  3067. {
  3068. return FontType.EastAsia;
  3069. }
  3070. return FontType.HAnsi;
  3071. }
  3072. // Unicode Block: Greek
  3073. if (ch >= 0x0370 && ch <= 0x03CF)
  3074. {
  3075. if (csa.Hint == "eastAsia")
  3076. {
  3077. return FontType.EastAsia;
  3078. }
  3079. return FontType.HAnsi;
  3080. }
  3081. // Unicode Block: Cyrillic
  3082. if (ch >= 0x0400 && ch <= 0x04FF)
  3083. {
  3084. if (csa.Hint == "eastAsia")
  3085. {
  3086. return FontType.EastAsia;
  3087. }
  3088. return FontType.HAnsi;
  3089. }
  3090. // Unicode Block: Hebrew
  3091. if (ch >= 0x0590 && ch <= 0x05FF)
  3092. {
  3093. return FontType.Ascii;
  3094. }
  3095. // Unicode Block: Arabic
  3096. if (ch >= 0x0600 && ch <= 0x06FF)
  3097. {
  3098. return FontType.Ascii;
  3099. }
  3100. // Unicode Block: Syriac
  3101. if (ch >= 0x0700 && ch <= 0x074F)
  3102. {
  3103. return FontType.Ascii;
  3104. }
  3105. // Unicode Block: Arabic Supplement
  3106. if (ch >= 0x0750 && ch <= 0x077F)
  3107. {
  3108. return FontType.Ascii;
  3109. }
  3110. // Unicode Block: Thanna
  3111. if (ch >= 0x0780 && ch <= 0x07BF)
  3112. {
  3113. return FontType.Ascii;
  3114. }
  3115. // Unicode Block: Hangul Jamo
  3116. if (ch >= 0x1100 && ch <= 0x11FF)
  3117. {
  3118. return FontType.EastAsia;
  3119. }
  3120. // Unicode Block: Latin Extended Additional
  3121. if (ch >= 0x1E00 && ch <= 0x1EFF)
  3122. {
  3123. if (csa.Hint == "eastAsia" &&
  3124. (csa.EastAsiaLang == "zh-hant" ||
  3125. csa.EastAsiaLang == "zh-hans"))
  3126. {
  3127. return FontType.EastAsia;
  3128. }
  3129. return FontType.HAnsi;
  3130. }
  3131. // Unicode Block: General Punctuation
  3132. if (ch >= 0x2000 && ch <= 0x206F)
  3133. {
  3134. if (csa.Hint == "eastAsia")
  3135. {
  3136. return FontType.EastAsia;
  3137. }
  3138. return FontType.HAnsi;
  3139. }
  3140. // Unicode Block: Superscripts and Subscripts
  3141. if (ch >= 0x2070 && ch <= 0x209F)
  3142. {
  3143. if (csa.Hint == "eastAsia")
  3144. {
  3145. return FontType.EastAsia;
  3146. }
  3147. return FontType.HAnsi;
  3148. }
  3149. // Unicode Block: Currency Symbols
  3150. if (ch >= 0x20A0 && ch <= 0x20CF)
  3151. {
  3152. if (csa.Hint == "eastAsia")
  3153. {
  3154. return FontType.EastAsia;
  3155. }
  3156. return FontType.HAnsi;
  3157. }
  3158. // Unicode Block: Combining Diacritical Marks for Symbols
  3159. if (ch >= 0x20D0 && ch <= 0x20FF)
  3160. {
  3161. if (csa.Hint == "eastAsia")
  3162. {
  3163. return FontType.EastAsia;
  3164. }
  3165. return FontType.HAnsi;
  3166. }
  3167. // Unicode Block: Letter-like Symbols
  3168. if (ch >= 0x2100 && ch <= 0x214F)
  3169. {
  3170. if (csa.Hint == "eastAsia")
  3171. {
  3172. return FontType.EastAsia;
  3173. }
  3174. return FontType.HAnsi;
  3175. }
  3176. // Unicode Block: Number Forms
  3177. if (ch >= 0x2150 && ch <= 0x218F)
  3178. {
  3179. if (csa.Hint == "eastAsia")
  3180. {
  3181. return FontType.EastAsia;
  3182. }
  3183. return FontType.HAnsi;
  3184. }
  3185. // Unicode Block: Arrows
  3186. if (ch >= 0x2190 && ch <= 0x21FF)
  3187. {
  3188. if (csa.Hint == "eastAsia")
  3189. {
  3190. return FontType.EastAsia;
  3191. }
  3192. return FontType.HAnsi;
  3193. }
  3194. // Unicode Block: Mathematical Operators
  3195. if (ch >= 0x2200 && ch <= 0x22FF)
  3196. {
  3197. if (csa.Hint == "eastAsia")
  3198. {
  3199. return FontType.EastAsia;
  3200. }
  3201. return FontType.HAnsi;
  3202. }
  3203. // Unicode Block: Miscellaneous Technical
  3204. if (ch >= 0x2300 && ch <= 0x23FF)
  3205. {
  3206. if (csa.Hint == "eastAsia")
  3207. {
  3208. return FontType.EastAsia;
  3209. }
  3210. return FontType.HAnsi;
  3211. }
  3212. // Unicode Block: Control Pictures
  3213. if (ch >= 0x2400 && ch <= 0x243F)
  3214. {
  3215. if (csa.Hint == "eastAsia")
  3216. {
  3217. return FontType.EastAsia;
  3218. }
  3219. return FontType.HAnsi;
  3220. }
  3221. // Unicode Block: Optical Character Recognition
  3222. if (ch >= 0x2440 && ch <= 0x245F)
  3223. {
  3224. if (csa.Hint == "eastAsia")
  3225. {
  3226. return FontType.EastAsia;
  3227. }
  3228. return FontType.HAnsi;
  3229. }
  3230. // Unicode Block: Enclosed Alphanumerics
  3231. if (ch >= 0x2460 && ch <= 0x24FF)
  3232. {
  3233. if (csa.Hint == "eastAsia")
  3234. {
  3235. return FontType.EastAsia;
  3236. }
  3237. return FontType.HAnsi;
  3238. }
  3239. // Unicode Block: Box Drawing
  3240. if (ch >= 0x2500 && ch <= 0x257F)
  3241. {
  3242. if (csa.Hint == "eastAsia")
  3243. {
  3244. return FontType.EastAsia;
  3245. }
  3246. return FontType.HAnsi;
  3247. }
  3248. // Unicode Block: Block Elements
  3249. if (ch >= 0x2580 && ch <= 0x259F)
  3250. {
  3251. if (csa.Hint == "eastAsia")
  3252. {
  3253. return FontType.EastAsia;
  3254. }
  3255. return FontType.HAnsi;
  3256. }
  3257. // Unicode Block: Geometric Shapes
  3258. if (ch >= 0x25A0 && ch <= 0x25FF)
  3259. {
  3260. if (csa.Hint == "eastAsia")
  3261. {
  3262. return FontType.EastAsia;
  3263. }
  3264. return FontType.HAnsi;
  3265. }
  3266. // Unicode Block: Miscellaneous Symbols
  3267. if (ch >= 0x2600 && ch <= 0x26FF)
  3268. {
  3269. if (csa.Hint == "eastAsia")
  3270. {
  3271. return FontType.EastAsia;
  3272. }
  3273. return FontType.HAnsi;
  3274. }
  3275. // Unicode Block: Dingbats
  3276. if (ch >= 0x2700 && ch <= 0x27BF)
  3277. {
  3278. if (csa.Hint == "eastAsia")
  3279. {
  3280. return FontType.EastAsia;
  3281. }
  3282. return FontType.HAnsi;
  3283. }
  3284. // Unicode Block: CJK Radicals Supplement
  3285. if (ch >= 0x2E80 && ch <= 0x2EFF)
  3286. {
  3287. if (csa.Hint == "eastAsia")
  3288. {
  3289. return FontType.EastAsia;
  3290. }
  3291. return FontType.HAnsi;
  3292. }
  3293. // Unicode Block: Kangxi Radicals
  3294. if (ch >= 0x2F00 && ch <= 0x2FDF)
  3295. {
  3296. return FontType.EastAsia;
  3297. }
  3298. // Unicode Block: Ideographic Description Characters
  3299. if (ch >= 0x2FF0 && ch <= 0x2FFF)
  3300. {
  3301. return FontType.EastAsia;
  3302. }
  3303. // Unicode Block: CJK Symbols and Punctuation
  3304. if (ch >= 0x3000 && ch <= 0x303F)
  3305. {
  3306. return FontType.EastAsia;
  3307. }
  3308. // Unicode Block: Hiragana
  3309. if (ch >= 0x3040 && ch <= 0x309F)
  3310. {
  3311. return FontType.EastAsia;
  3312. }
  3313. // Unicode Block: Katakana
  3314. if (ch >= 0x30A0 && ch <= 0x30FF)
  3315. {
  3316. return FontType.EastAsia;
  3317. }
  3318. // Unicode Block: Bopomofo
  3319. if (ch >= 0x3100 && ch <= 0x312F)
  3320. {
  3321. return FontType.EastAsia;
  3322. }
  3323. // Unicode Block: Hangul Compatibility Jamo
  3324. if (ch >= 0x3130 && ch <= 0x318F)
  3325. {
  3326. return FontType.EastAsia;
  3327. }
  3328. // Unicode Block: Kanbun
  3329. if (ch >= 0x3190 && ch <= 0x319F)
  3330. {
  3331. return FontType.EastAsia;
  3332. }
  3333. // Unicode Block: Enclosed CJK Letters and Months
  3334. if (ch >= 0x3200 && ch <= 0x32FF)
  3335. {
  3336. return FontType.EastAsia;
  3337. }
  3338. // Unicode Block: CJK Compatibility
  3339. if (ch >= 0x3300 && ch <= 0x33FF)
  3340. {
  3341. return FontType.EastAsia;
  3342. }
  3343. // Unicode Block: CJK Unified Ideographs Extension A
  3344. if (ch >= 0x3400 && ch <= 0x4DBF)
  3345. {
  3346. return FontType.EastAsia;
  3347. }
  3348. // Unicode Block: CJK Unified Ideographs
  3349. if (ch >= 0x4E00 && ch <= 0x9FAF)
  3350. {
  3351. return FontType.EastAsia;
  3352. }
  3353. // Unicode Block: Yi Syllables
  3354. if (ch >= 0xA000 && ch <= 0xA48F)
  3355. {
  3356. return FontType.EastAsia;
  3357. }
  3358. // Unicode Block: Yi Radicals
  3359. if (ch >= 0xA490 && ch <= 0xA4CF)
  3360. {
  3361. return FontType.EastAsia;
  3362. }
  3363. // Unicode Block: Hangul Syllables
  3364. if (ch >= 0xAC00 && ch <= 0xD7AF)
  3365. {
  3366. return FontType.EastAsia;
  3367. }
  3368. // Unicode Block: High Surrogates
  3369. if (ch >= 0xD800 && ch <= 0xDB7F)
  3370. {
  3371. return FontType.EastAsia;
  3372. }
  3373. // Unicode Block: High Private Use Surrogates
  3374. if (ch >= 0xDB80 && ch <= 0xDBFF)
  3375. {
  3376. return FontType.EastAsia;
  3377. }
  3378. // Unicode Block: Low Surrogates
  3379. if (ch >= 0xDC00 && ch <= 0xDFFF)
  3380. {
  3381. return FontType.EastAsia;
  3382. }
  3383. // Unicode Block: Private Use Area
  3384. if (ch >= 0xE000 && ch <= 0xF8FF)
  3385. {
  3386. if (csa.Hint == "eastAsia")
  3387. {
  3388. return FontType.EastAsia;
  3389. }
  3390. return FontType.HAnsi;
  3391. }
  3392. // Unicode Block: CJK Compatibility Ideographs
  3393. if (ch >= 0xF900 && ch <= 0xFAFF)
  3394. {
  3395. return FontType.EastAsia;
  3396. }
  3397. // Unicode Block: Alphabetic Presentation Forms
  3398. if (ch >= 0xFB00 && ch <= 0xFB4F)
  3399. {
  3400. if (csa.Hint == "eastAsia")
  3401. {
  3402. if (ch >= 0xFB00 && ch <= 0xFB1C)
  3403. return FontType.EastAsia;
  3404. if (ch >= 0xFB1D && ch <= 0xFB4F)
  3405. return FontType.Ascii;
  3406. }
  3407. return FontType.HAnsi;
  3408. }
  3409. // Unicode Block: Arabic Presentation Forms-A
  3410. if (ch >= 0xFB50 && ch <= 0xFDFF)
  3411. {
  3412. return FontType.Ascii;
  3413. }
  3414. // Unicode Block: CJK Compatibility Forms
  3415. if (ch >= 0xFE30 && ch <= 0xFE4F)
  3416. {
  3417. return FontType.EastAsia;
  3418. }
  3419. // Unicode Block: Small Form Variants
  3420. if (ch >= 0xFE50 && ch <= 0xFE6F)
  3421. {
  3422. return FontType.EastAsia;
  3423. }
  3424. // Unicode Block: Arabic Presentation Forms-B
  3425. if (ch >= 0xFE70 && ch <= 0xFEFE)
  3426. {
  3427. return FontType.Ascii;
  3428. }
  3429. // Unicode Block: Halfwidth and Fullwidth Forms
  3430. if (ch >= 0xFF00 && ch <= 0xFFEF)
  3431. {
  3432. return FontType.EastAsia;
  3433. }
  3434. return FontType.HAnsi;
  3435. }
  3436. private class FormattingAssemblerInfo
  3437. {
  3438. public string DefaultParagraphStyleName;
  3439. public string DefaultCharacterStyleName;
  3440. public string DefaultTableStyleName;
  3441. public Dictionary<string, XElement> RolledCharacterStyles;
  3442. public FormattingAssemblerInfo()
  3443. {
  3444. RolledCharacterStyles = new Dictionary<string, XElement>();
  3445. }
  3446. }
  3447. // CachedParaInfo is an optimization for the case where a paragraph contains thousands of runs.
  3448. private class CachedParaInfo
  3449. {
  3450. public string ParagraphStyleName;
  3451. public XElement ParagraphProperties;
  3452. }
  3453. public class UnsupportedNumberingFormatException : Exception
  3454. {
  3455. public UnsupportedNumberingFormatException(string message) : base(message) { }
  3456. }
  3457. public class UnsupportedLanguageException : Exception
  3458. {
  3459. public UnsupportedLanguageException(string message) : base(message) { }
  3460. }
  3461. }
  3462. }