FormattingAssembler.cs 142 KB

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