123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200 |
- // Copyright (c) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Xml;
- using System.Xml.Linq;
- using System.Xml.Xsl;
- using DocumentFormat.OpenXml;
- using DocumentFormat.OpenXml.Drawing;
- using DocumentFormat.OpenXml.Drawing.Charts;
- using DocumentFormat.OpenXml.Office2010.Word.DrawingShape;
- using DocumentFormat.OpenXml.Packaging;
- using HTEXLib.COMM.Helpers;
- using HTEXLib.DOCX.OpenXmlTool;
- using HTEXLib.Helpers;
- using HTEXLib.Helpers.ShapeHelpers;
- using HTEXLib.Models.Inner;
- // 200e lrm - LTR
- // 200f rlm - RTL
- // todo need to set the HTTP "Content-Language" header, for instance:
- // Content-Language: en-US
- // Content-Language: fr-FR
- namespace OpenXmlPowerTools
- {
- public partial class WmlDocument
- {
- [SuppressMessage("ReSharper", "UnusedMember.Global")]
- public XElement ConvertToHtml(WmlToHtmlConverterSettings htmlConverterSettings)
- {
- return WmlToHtmlConverter.ConvertToHtml(this, htmlConverterSettings);
- }
- [SuppressMessage("ReSharper", "UnusedMember.Global")]
- public XElement ConvertToHtml(HtmlConverterSettings htmlConverterSettings)
- {
- WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings);
- return WmlToHtmlConverter.ConvertToHtml(this, settings);
- }
- }
- [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
- public class WmlToHtmlConverterSettings
- {
- public string PageTitle;
- public bool TableInnerCss;
- public string CssClassPrefix;
- public bool FabricateCssClasses;
- public string GeneralCss;
- public string AdditionalCss;
- public bool RestrictToSupportedLanguages;
- public bool RestrictToSupportedNumberingFormats;
- public Dictionary<string, Func<string, int, string, string>> ListItemImplementations;
- public Func<ImageInfo, XElement> ImageHandler;
- public WmlToHtmlConverterSettings()
- {
- PageTitle = "";
- CssClassPrefix = "pt-";
- TableInnerCss = true;
- FabricateCssClasses = true;
- GeneralCss = "span {/* white-space: pre-wrap;*/ }";
- AdditionalCss = "";
- RestrictToSupportedLanguages = false;
- RestrictToSupportedNumberingFormats = false;
- ListItemImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations;
- }
- public WmlToHtmlConverterSettings(HtmlConverterSettings htmlConverterSettings)
- {
- TableInnerCss = htmlConverterSettings.TableInnerCss;
- PageTitle = htmlConverterSettings.PageTitle;
- CssClassPrefix = htmlConverterSettings.CssClassPrefix;
- FabricateCssClasses = htmlConverterSettings.FabricateCssClasses;
- GeneralCss = htmlConverterSettings.GeneralCss;
- AdditionalCss = htmlConverterSettings.AdditionalCss;
- RestrictToSupportedLanguages = htmlConverterSettings.RestrictToSupportedLanguages;
- RestrictToSupportedNumberingFormats = htmlConverterSettings.RestrictToSupportedNumberingFormats;
- ListItemImplementations = htmlConverterSettings.ListItemImplementations;
- ImageHandler = htmlConverterSettings.ImageHandler;
- }
- }
- [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
- public class HtmlConverterSettings
- {
- public bool TableInnerCss;
- public string PageTitle;
- public string CssClassPrefix;
- public bool FabricateCssClasses;
- public string GeneralCss;
- public string AdditionalCss;
- public bool RestrictToSupportedLanguages;
- public bool RestrictToSupportedNumberingFormats;
- public Dictionary<string, Func<string, int, string, string>> ListItemImplementations;
- public Func<ImageInfo, XElement> ImageHandler;
- public HtmlConverterSettings()
- {
- TableInnerCss = true;
- PageTitle = "";
- CssClassPrefix = "pt-";
- FabricateCssClasses = true;
- GeneralCss = "span { /*white-space: pre-wrap;*/ }";
- AdditionalCss = "";
- RestrictToSupportedLanguages = false;
- RestrictToSupportedNumberingFormats = false;
- ListItemImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations;
- }
- }
- public static class HtmlConverter
- {
- public static XElement ConvertToHtml(WmlDocument wmlDoc, HtmlConverterSettings htmlConverterSettings)
- {
- WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings);
- return WmlToHtmlConverter.ConvertToHtml(wmlDoc, settings);
- }
- public static XElement ConvertToHtml(WordprocessingDocument wDoc, HtmlConverterSettings htmlConverterSettings)
- {
- WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings);
- return WmlToHtmlConverter.ConvertToHtml(wDoc, settings);
- }
- }
- [SuppressMessage("ReSharper", "NotAccessedField.Global")]
- [SuppressMessage("ReSharper", "UnusedMember.Global")]
- public class ImageInfo
- {
- public string base64;
- public Bitmap Bitmap;
- public XAttribute ImgStyleAttribute;
- public string ContentType;
- public XElement DrawingElement;
- public string AltText;
- public string Mathxml;
- public const int EmusPerInch = 914400;
- public const int EmusPerCm = 360000;
- }
- public static class WmlToHtmlConverter
- {
- public static XElement ConvertToHtml(WmlDocument doc, WmlToHtmlConverterSettings htmlConverterSettings)
- {
- using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
- {
- using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
- {
- return ConvertToHtml(document, htmlConverterSettings);
- }
- }
- }
- public static XElement ConvertToHtml(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings htmlConverterSettings)
- {
- RevisionAccepter.AcceptRevisions(wordDoc);
- SimplifyMarkupSettings simplifyMarkupSettings = new SimplifyMarkupSettings
- {
- RemoveComments = true,
- RemoveContentControls = true,
- RemoveEndAndFootNotes = true,
- RemoveFieldCodes = false,
- RemoveLastRenderedPageBreak = true,
- RemovePermissions = true,
- RemoveProof = true,
- RemoveRsidInfo = true,
- RemoveSmartTags = true,
- RemoveSoftHyphens = true,
- RemoveGoBackBookmark = true,
- ReplaceTabsWithSpaces = false,
- };
- MarkupSimplifier.SimplifyMarkup(wordDoc, simplifyMarkupSettings);
- FormattingAssemblerSettings formattingAssemblerSettings = new FormattingAssemblerSettings
- {
- RemoveStyleNamesFromParagraphAndRunProperties = false,
- ClearStyles = false,
- RestrictToSupportedLanguages = htmlConverterSettings.RestrictToSupportedLanguages,
- RestrictToSupportedNumberingFormats = htmlConverterSettings.RestrictToSupportedNumberingFormats,
- CreateHtmlConverterAnnotationAttributes = true,
- OrderElementsPerStandard = false,
- ListItemRetrieverSettings =
- htmlConverterSettings.ListItemImplementations == null ?
- new ListItemRetrieverSettings()
- {
- ListItemTextImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations,
- } :
- new ListItemRetrieverSettings()
- {
- ListItemTextImplementations = htmlConverterSettings.ListItemImplementations,
- },
- };
- FormattingAssembler.AssembleFormatting(wordDoc, formattingAssemblerSettings);
- InsertAppropriateNonbreakingSpaces(wordDoc);
- CalculateSpanWidthForTabs(wordDoc);
- ReverseTableBordersForRtlTables(wordDoc);
- AdjustTableBorders(wordDoc);
- XElement rootElement = wordDoc.MainDocumentPart.GetXDocument().Root;
- FieldRetriever.AnnotateWithFieldInfo(wordDoc.MainDocumentPart);
- AnnotateForSections(wordDoc);
- XElement xhtml = (XElement)ConvertToHtmlTransform(wordDoc, htmlConverterSettings,
- rootElement, false, 0m);
- ReifyStylesAndClasses(htmlConverterSettings, xhtml);
- // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type
- // XEntity. PtOpenXmlUtil.cs define the XEntity class. See
- // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx
- // for detailed explanation.
- //
- // If you further transform the XML tree returned by ConvertToHtmlTransform, you
- // must do it correctly, or entities will not be serialized properly.
- return xhtml;
- }
- private static void ReverseTableBordersForRtlTables(WordprocessingDocument wordDoc)
- {
- XDocument xd = wordDoc.MainDocumentPart.GetXDocument();
- foreach (var tbl in xd.Descendants(W.tbl))
- {
- var bidiVisual = tbl.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
- if (bidiVisual == null)
- continue;
- var tblBorders = tbl.Elements(W.tblPr).Elements(W.tblBorders).FirstOrDefault();
- if (tblBorders != null)
- {
- var left = tblBorders.Element(W.left);
- if (left != null)
- left = new XElement(W.right, left.Attributes());
- var right = tblBorders.Element(W.right);
- if (right != null)
- right = new XElement(W.left, right.Attributes());
- var newTblBorders = new XElement(W.tblBorders,
- tblBorders.Element(W.top),
- left,
- tblBorders.Element(W.bottom),
- right);
- tblBorders.ReplaceWith(newTblBorders);
- }
- foreach (var tc in tbl.Elements(W.tr).Elements(W.tc))
- {
- var tcBorders = tc.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault();
- if (tcBorders != null)
- {
- var left = tcBorders.Element(W.left);
- if (left != null)
- left = new XElement(W.right, left.Attributes());
- var right = tcBorders.Element(W.right);
- if (right != null)
- right = new XElement(W.left, right.Attributes());
- var newTcBorders = new XElement(W.tcBorders,
- tcBorders.Element(W.top),
- left,
- tcBorders.Element(W.bottom),
- right);
- tcBorders.ReplaceWith(newTcBorders);
- }
- }
- }
- }
- private static void ReifyStylesAndClasses(WmlToHtmlConverterSettings htmlConverterSettings, XElement xhtml)
- {
- if (htmlConverterSettings.FabricateCssClasses)
- {
- var usedCssClassNames = new HashSet<string>();
- var elementsThatNeedClasses = xhtml
- .DescendantsAndSelf()
- .Select(d => new
- {
- Element = d,
- Styles = d.Annotation<Dictionary<string, string>>(),
- })
- .Where(z => z.Styles != null);
- var augmented = elementsThatNeedClasses
- .Select(p => new
- {
- p.Element,
- p.Styles,
- StylesString = p.Element.Name.LocalName + "|" + p.Styles.OrderBy(k => k.Key).Select(s => string.Format("{0}: {1};", s.Key, s.Value)).StringConcatenate(),
- })
- .GroupBy(p => p.StylesString)
- .ToList();
- int classCounter = 1000000;
- var sb = new StringBuilder();
- sb.Append(Environment.NewLine);
- foreach (var grp in augmented)
- {
- string classNameToUse;
- var firstOne = grp.First();
- var styles = firstOne.Styles;
- if (styles.ContainsKey("PtStyleName"))
- {
- classNameToUse = htmlConverterSettings.CssClassPrefix + styles["PtStyleName"];
- if (usedCssClassNames.Contains(classNameToUse))
- {
- classNameToUse = htmlConverterSettings.CssClassPrefix +
- styles["PtStyleName"] + "-" +
- classCounter.ToString().Substring(1);
- classCounter++;
- }
- }
- else
- {
- classNameToUse = htmlConverterSettings.CssClassPrefix +
- classCounter.ToString().Substring(1);
- classCounter++;
- }
- usedCssClassNames.Add(classNameToUse);
- sb.Append(firstOne.Element.Name.LocalName + "." + classNameToUse + " {" + Environment.NewLine);
- foreach (var st in firstOne.Styles.Where(s => s.Key != "PtStyleName"))
- {
- var s = " " + st.Key + ": " + st.Value + ";" + Environment.NewLine;
- sb.Append(s);
- }
- sb.Append("}" + Environment.NewLine);
- var classAtt = new XAttribute("class", classNameToUse);
- foreach (var gc in grp)
- gc.Element.Add(classAtt);
- }
- var styleValue = htmlConverterSettings.GeneralCss + sb + htmlConverterSettings.AdditionalCss;
- SetStyleElementValue(xhtml, styleValue);
- }
- else
- {
- // Previously, the h:style element was not added at this point. However,
- // at least the General CSS will contain important settings.
- SetStyleElementValue(xhtml, htmlConverterSettings.GeneralCss + htmlConverterSettings.AdditionalCss);
- foreach (var d in xhtml.DescendantsAndSelf())
- {
-
- var style = d.Annotation<Dictionary<string, string>>();
- if (style == null)
- continue;
- var styleValue =
- style
- .Where(p => p.Key != "PtStyleName")
- .OrderBy(p => p.Key)
- .Select(e => string.Format("{0}: {1};", e.Key, e.Value))
- .StringConcatenate();
- XAttribute st = new XAttribute("style", styleValue);
- if (d.Attribute("style") != null)
- d.Attribute("style").Value += styleValue;
- else
- d.Add(st);
- }
- }
- if (htmlConverterSettings.TableInnerCss) {
- SetStyleElementValue(xhtml, htmlConverterSettings.GeneralCss + htmlConverterSettings.AdditionalCss);
- foreach (var d in xhtml.DescendantsAndSelf())
- {
- if (d.Name.LocalName.ToString().Equals("table") || d.Name.LocalName.ToString().Equals("tr") ||d.Name.LocalName.ToString().Equals("td")) {
- var style = d.Annotation<Dictionary<string, string>>();
- if (style == null)
- continue;
- var styleValue =
- style
- .Where(p => p.Key != "PtStyleName")
- .OrderBy(p => p.Key)
- .Select(e => string.Format("{0}: {1};", e.Key, e.Value))
- .StringConcatenate();
- XAttribute st = new XAttribute("style", styleValue);
- if (d.Attribute("style") != null)
- d.Attribute("style").Value += styleValue;
- else
- d.Add(st);
- }
- }
- }
- }
- private static void SetStyleElementValue(XElement xhtml, string styleValue)
- {
- var styleElement = xhtml
- .Descendants(Xhtml.style)
- .FirstOrDefault();
- if (styleElement != null)
- styleElement.Value = styleValue;
- else
- {
- styleElement = new XElement(Xhtml.style, styleValue);
- var head = xhtml.Element(Xhtml.head);
- if (head != null)
- head.Add(styleElement);
- }
- }
- private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc,
- WmlToHtmlConverterSettings settings, XNode node,
- bool suppressTrailingWhiteSpace,
- decimal currentMarginLeft)
- {
- var element = node as XElement;
- if (element == null) return null;
- // Transform the w:document element to the XHTML h:html element.
- // The h:head element is laid out based on the W3C's recommended layout, i.e.,
- // the charset (using the HTML5-compliant form), the title (which is always
- // there but possibly empty), and other meta tags.
- if (element.Name == W.document)
- {
- return new XElement(Xhtml.html,
- new XElement(Xhtml.head,
- new XElement(Xhtml.meta, new XAttribute("charset", "UTF-8")),
- settings.PageTitle != null
- ? new XElement(Xhtml.title, new XText(settings.PageTitle))
- : new XElement(Xhtml.title, new XText(string.Empty)),
- new XElement(Xhtml.meta,
- new XAttribute("name", "Generator"),
- new XAttribute("content", "PowerTools for Open XML"))),
- element.Elements()
- .Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
- }
- // Transform the w:body element to the XHTML h:body element.
- if (element.Name == W.body)
- {
- return new XElement(Xhtml.body, CreateSectionDivs(wordDoc, settings, element));
- }
- // Transform the w:p element to the XHTML h:h1-h6 or h:p element (if the previous paragraph does not
- // have a style separator).
- if (element.Name == W.p)
- {
- return ProcessParagraph(wordDoc, settings, element, suppressTrailingWhiteSpace, currentMarginLeft);
- }
- // Transform hyperlinks to the XHTML h:a element.
- if (element.Name == W.hyperlink && element.Attribute(R.id) != null)
- {
- try
- {
- var a = new XElement(Xhtml.a,
- new XAttribute("href",
- wordDoc.MainDocumentPart
- .HyperlinkRelationships
- .First(x => x.Id == (string)element.Attribute(R.id))
- .Uri
- ),
- element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run))
- );
- if (!a.Nodes().Any())
- a.Add(new XText(""));
- return a;
- }
- catch (UriFormatException)
- {
- return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
- }
- }
- // Transform hyperlinks to bookmarks to the XHTML h:a element.
- if (element.Name == W.hyperlink && element.Attribute(W.anchor) != null)
- {
- return ProcessHyperlinkToBookmark(wordDoc, settings, element);
- }
- // Transform contents of runs.
- if (element.Name == W.r)
- {
- return ConvertRun(wordDoc, settings, element);
- }
- // Transform w:bookmarkStart into anchor
- if (element.Name == W.bookmarkStart)
- {
- return ProcessBookmarkStart(element);
- }
- // Transform every w:t element to a text node.
- if (element.Name == W.t)
- {
- // We don't need to convert characters to entities in a UTF-8 document.
- // Further, we don't need entities for significant whitespace
- // because we are wrapping the text nodes in <span> elements within
- // which all whitespace is significant.
- return new XText(element.Value);
- }
- // Transform symbols to spans
- if (element.Name == W.sym)
- {
- var cs = (string)element.Attribute(W._char);
- var c = Convert.ToInt32(cs, 16);
- return new XElement(Xhtml.span, new XEntity(string.Format("#{0}", c)));
- }
- // Transform tabs that have the pt:TabWidth attribute set
- if (element.Name == W.tab)
- {
- return ProcessTab(element);
- }
- // Transform w:br to h:br.
- if (element.Name == W.br || element.Name == W.cr)
- {
- return ProcessBreak(element);
- }
- // Transform w:noBreakHyphen to '-'
- if (element.Name == W.noBreakHyphen)
- {
- return new XText("-");
- }
- // Transform w:tbl to h:tbl.
- if (element.Name == W.tbl)
- {
- return ProcessTable(wordDoc, settings, element, currentMarginLeft);
- }
- // Transform w:tr to h:tr.
- if (element.Name == W.tr)
- {
- return ProcessTableRow(wordDoc, settings, element, currentMarginLeft);
- }
- // Transform w:tc to h:td.
- if (element.Name == W.tc)
- {
- return ProcessTableCell(wordDoc, settings, element);
- }
- // Transform images
- if (element.Name == W.drawing || element.Name == W.pict || element.Name == W._object)
- {
- XElement xElement = ProcessImage(wordDoc, element, settings.ImageHandler);
- return xElement;
- }
- // Transform content controls.
- if (element.Name == W.sdt)
- {
- return ProcessContentControl(wordDoc, settings, element, currentMarginLeft);
- }
- // Transform smart tags and simple fields.
- if (element.Name == W.smartTag || element.Name == W.fldSimple)
- {
- return CreateBorderDivs(wordDoc, settings, element.Elements());
- }
- //处理 数学公式
- if (element.Name == M.oMath)
- {
- return ProcessOMath(element);
- }
- if (element.Name == M.oMathPara)
- {
- return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
- }
- //处理形状
- if (element.Name == MC.AlternateContent)
- {
- return ProcessAlternateContent(wordDoc, element,settings.ImageHandler);
- }
- //if (element.Name == MC.Choice)
- //{
- // return ProcessOMath(element);
- //}
- //if (element.Name == W.drawing &&element.Parent.Name==MC.Choice)
- //{
- // return ProcessOMath(element);
- //}
- // Ignore element.
- return null;
- }
- public static XElement ProcessAlternateContent(WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> imageHandler) {
- var drawing = element.Element(MC.Choice).Element(W.drawing).Element(WP.inline);
- if (drawing == null) {
- drawing = element.Element(MC.Choice).Element(W.drawing).Element(WP.anchor);
- }
- var graphicData = drawing.Element(A.graphic).Element(A.graphicData).Elements() ;
-
- foreach (var elm in graphicData) {
- if (elm.Name==WPS.wsp)
- {
- var html = ProcessWps(wordDoc, elm, imageHandler);
- XElement elmt = XElement.Parse(html);
- return elmt;
- }
- else if (elm.Name==WPG.wgp)
- {
- return ProcessWgp(wordDoc, elm, imageHandler);
- }
- }
- return null;
- }
- public static XElement ProcessWgp(WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> imageHandler) {
- var extentCx = (int?)element.Elements(WP.extent)
- .Attributes(NoNamespace.cx).FirstOrDefault();
- var extentCy = (int?)element.Elements(WP.extent)
- .Attributes(NoNamespace.cy).FirstOrDefault();
- var elms= element.Descendants(WPS.wsp);
- StringBuilder stringBuilder = new StringBuilder();
- foreach (var elm in elms) {
- var emlt= ProcessWps(wordDoc, elm, imageHandler);
- stringBuilder.Append(emlt);
- }
- XElement elmt = XElement.Parse(stringBuilder.ToString());
- return elmt;
- }
-
- public static XElement ProcessChart(WordprocessingDocument wordDoc, XElement containerElement, Func<ImageInfo, XElement> imageHandler,
- int? extentCx, int? extentCy, string altText, XElement element, string hyperlinkUri)
- {
- var ird= element.Attribute(R.id);
- var chartird= wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp => pp.RelationshipId == ird.Value);
- ChartPart chartPart= wordDoc.MainDocumentPart.ChartParts.Where(x => x.Uri.ToString() == chartird.OpenXmlPart.Uri.ToString()).FirstOrDefault<ChartPart>();
- if (chartPart != null)
- {
- ChartColorStylePart ChartColorStylePart = null;
- ChartStylePart ChartStylePart = null;
- foreach (var idp in chartPart.Parts)
- {
- if (idp.OpenXmlPart is ChartColorStylePart ChartColorStyleParts)
- {
- ChartColorStylePart = ChartColorStyleParts;
- }
- if (idp.OpenXmlPart is ChartStylePart ChartStyleParts)
- {
- ChartStylePart = ChartStyleParts;
- }
- }
- if (ChartStylePart != null)
- {
- var ChartStyleChildren = ChartStylePart.ChartStyle.ChildElements;
- foreach (var child in ChartStyleChildren)
- {
- if (child is DocumentFormat.OpenXml.Office2013.Drawing.ChartStyle.StyleEntry StyleEntry)
- {
- // DoStyleEntry(StyleEntry);
- }
- ///OfficeArtExtensionList cs: extLst
- // MarkerLayoutProperties cs: dataPointMarkerLayout
- }
- }
- HTEXLib. Chart charts = DrawChart(wordDoc,chartPart.ChartSpace,element,imageHandler,null);
- // charts.links = slide.hyperlinks;
- // return new List<Item> { charts };
- }
- return null;
- }
-
-
- public static HTEXLib.Chart DrawChart(WordprocessingDocument wordDoc, DocumentFormat.OpenXml.Drawing.Charts.ChartSpace chartSpace,XElement element, Func<ImageInfo, XElement> ImageHandler, HTEXLib.Position positionc)
- {
- var Chart = chartSpace.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Chart>();
- var ShapeProperties = chartSpace.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.ShapeProperties>();
- HTEXLib.Models.HTEX.ShapeStyle shapeStyleChart = WORDHelper.DoShapeProperties(wordDoc ,ShapeProperties, element, ImageHandler, positionc);
- var TextProperties = chartSpace.GetFirstChild<TextProperties>();
- var style = chartSpace.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Style>();
- var ColorMapOverride = chartSpace.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.ColorMapOverride>();
- var style2010 = chartSpace.GetFirstChild<DocumentFormat.OpenXml.Office2010.Drawing.Charts.Style>();
- var UserShapesReference = chartSpace.GetFirstChild<UserShapesReference>();
- var charts = DoPlotArea(Chart.PlotArea,wordDoc,element, ImageHandler,positionc);
- HTEXLib.Models.HTEX. ShapeStyle shapeStyle = null;
- if (Chart.Title != null)
- {
- var ChartShapeProperties = Chart.Title.ChartShapeProperties;
- shapeStyle = WORDHelper.DoShapeProperties(wordDoc, ChartShapeProperties, element, ImageHandler, positionc);
- }
- // var (PPTParagraphs, tBody) = DoChartTitle(Chart.Title);
- //TextBody textBody = DrawText(PPTParagraphs, tBody);
- //Shape shape= new Shape { type = "Sp",uid ="Sp-" +ShaHashHelper.GetSHA1(slide.slideIndex + "-" + (Chart.Title.GetType().Name + Chart.Title.OuterXml).Trim().ToLower()), shapeType = "rect",
- // textBody = textBody , sid = sid + "titel" };
- HTEXLib. Shape shape = new HTEXLib.Shape
- {
- type = "Sp",
- uid = Guid.NewGuid().ToString(),
- shapeType = "rect",
- // textBody = textBody,
- // sid = sid + "titel"
- };
- shape.style.fill = shapeStyle != null ? shapeStyle.fill : null;
- shape.style.border = shapeStyle != null ? shapeStyle.border : null;
- //var chart= new Chart {sid=sid,charts =charts,title=shape,type=type,uid = type+"-"+ ShaHashHelper.GetSHA1(slide.slideIndex + "-" + (Chart.GetType().Name + Chart.OuterXml).Trim().ToLower()) };
- var chart = new HTEXLib.Chart
- {
- // sid = sid,
- charts = charts,
- title = shape,
- // type = type,
- uid = Guid.NewGuid().ToString(),
- };
- HTEXLib.Position position = new HTEXLib.Position { /*cx = width, cy = height, x = left, y = top, rot = rot */};
- chart.style.position = position;
- chart.style.fill = shapeStyleChart != null ? shapeStyleChart.fill : null;
- chart.style.border = shapeStyleChart != null ? shapeStyleChart.border : null;
- chart.style.effect = shapeStyleChart != null ? shapeStyleChart.effect : null;
- chart.svg = new HTEXLib.Svg
- {
- type = "path",
- d = "M" + 0 + " " + 0 + ",L" + position.cx + " " + 0 + ",L" + position.cx + " " + position.cy + ",L" + 0 + " " + position.cy + " z",
- close = true
- //new Rect {
- // x=position.x,
- // y = position.y,
- // width=position.cx,
- // height=position.cy,
- // type="rect"
- // }
- };
- if (chart.style != null)
- {
- if (chart.style.fill.type == -1 || chart.style.fill.type == 0)
- {
- chart.style.fill = null;
- }
- if (chart.style.border != null && chart.style.border.type == "none")
- {
- chart.style.border = null;
- }
- }
- return chart;
- }
- public static List<HTEXLib. CommonChart> DoPlotArea(PlotArea plotArea, WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> ImageHandler, HTEXLib.Position positionc)
- {
- List<HTEXLib.CommonChart> charts = new List<HTEXLib.CommonChart>();
- foreach (var child in plotArea.ChildElements)
- {
- string key = child.LocalName;
- IEnumerable<XElement> serNodes = null;
- switch (key)
- {
- //break块中不可以随意更换,此条件用于归类不同从Chart
- case "pieChart":
- case "ofPieChart":
- case "pie3DChart":
- case "doughnutChart":
- HTEXLib.PieChart pieChart = new HTEXLib.PieChart { chartType = "pie" };
- if (key.Equals("pie3DChart"))
- {
- pieChart.is3D = true;
- }
- pieChart.pieType = key;
- if (key.Equals("ofPieChart"))
- {
- var ofPieType = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:ofPieType");
- if (ofPieType != null)
- {
- //ofPieChart-pie ofPieChart-bar 子母饼图
- pieChart.pieType += "-" + ofPieType.Attribute("val").Value;
- }
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser") ;
- pieChart.datas = ExtractChartData(serNodes,wordDoc,element, ImageHandler, positionc);
- charts.Add(pieChart);
- break;
- case "lineChart":
- case "line3DChart":
- HTEXLib.LineChart lineChart = new HTEXLib.LineChart { chartType = "line" };
- if (key.Equals("line3DChart"))
- {
- lineChart.is3D = true;
- }
- lineChart.lineType = key;
- var LineGrouping = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:grouping");
- if (LineGrouping != null)
- {
- //standard stacked percentStacked
- lineChart.lineType += "-" + LineGrouping.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- lineChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(lineChart);
- break;
- case "barChart":
- case "bar3DChart":
- var barDir = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:barDir");
- if (barDir != null)
- {
- if (barDir.Attribute("val").Value.Equals("bar"))
- {
- HTEXLib.BarChart barChart = new HTEXLib.BarChart { chartType = "bar" };
- charts.Add(barChart);
- if (key.Equals("bar3DChart"))
- {
- barChart.is3D = true;
- }
- barChart.barType = key;
- var BarGrouping = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:grouping");
- if (BarGrouping != null)
- {
- //standard stacked percentStacked
- barChart.barType += "-" + BarGrouping.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- barChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(barChart);
- }
- else if (barDir.Attribute("val").Value.Equals("col"))
- {
- HTEXLib.ColChart colChart = new HTEXLib.ColChart { chartType = "col" };
- if (key.Equals("bar3DChart"))
- {
- colChart.is3D = true;
- }
- colChart.colType = key.Replace("bar", "col");
- var ColGrouping = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:grouping");
- if (ColGrouping != null)
- {
- //standard stacked percentStacked
- colChart.colType += "-" + ColGrouping.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- colChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(colChart);
- }
- }
- break;
- case "areaChart":
- case "area3DChart":
- HTEXLib.AreaChart areaChart = new HTEXLib.AreaChart { chartType = "area" };
- if (key.Equals("area3DChart"))
- {
- areaChart.is3D = true;
- }
- areaChart.areaType = key;
- var AreaGrouping = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:grouping");
- if (AreaGrouping != null)
- {
- //standard stacked percentStacked
- areaChart.areaType += "-" + AreaGrouping.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- areaChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(areaChart);
- break;
- case "scatterChart":
- case "bubbleChart":
- HTEXLib.ScatterChart scatterChart = new HTEXLib.ScatterChart { chartType = "scatter" };
- scatterChart.scatterType = key;
- if (key.Equals("scatterChart"))
- {
- var ScatterStyle = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:scatterStyle");
- if (ScatterStyle != null)
- {
- scatterChart.scatterType += "-" + ScatterStyle.Attribute("val").Value.Replace("Marker", "");
- }
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- scatterChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(scatterChart);
- break;
- case "radarChart":
- HTEXLib.RadarChart radarChart = new HTEXLib.RadarChart { chartType = "radar" };
- radarChart.radarType = key;
- var RadarStyle = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:radarStyle");
- if (RadarStyle != null)
- {
- radarChart.radarType += "-" + RadarStyle.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- radarChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(radarChart);
- break;
- case "plotAreaRegion":
- HTEXLib.PlotAreaChart plotAreaChart = new HTEXLib.PlotAreaChart { chartType = "plotArea" };
- plotAreaChart.plotAreaType = key;
- var PlotSeries = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "cx:series");
- if (PlotSeries != null)
- {
- plotAreaChart.plotAreaType += "-" + PlotSeries.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- plotAreaChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(plotAreaChart);
- break;
- case "stockChart":
- HTEXLib.StockChart stockChart = new HTEXLib.StockChart { chartType = "stock" };
- stockChart.stockType = key;
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- stockChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(stockChart);
- break;
- case "surfaceChart":
- case "surface3DChart":
- HTEXLib.SurfaceChart surfaceChart = new HTEXLib.SurfaceChart { chartType = "surface" };
- if (key.Equals("surface3DChart"))
- {
- surfaceChart.is3D = true;
- }
- surfaceChart.surfaceType = key;
- var Wireframe = HTEXLib.ShapeHelper.GetPPTXNodeByPath(child, "c:wireframe");
- if (Wireframe != null)
- {
- surfaceChart.surfaceType += "-" + Wireframe.Attribute("val").Value;
- }
- serNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(child, "c:ser");
- surfaceChart.datas = ExtractChartData(serNodes, wordDoc, element, ImageHandler, positionc);
- charts.Add(surfaceChart);
- break;
- }
- }
- return charts;
- }
- public static List<Dictionary<string, object>> ExtractChartData(IEnumerable<XElement> nodes, WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> ImageHandler, HTEXLib.Position positionc)
- {
- if (nodes != null)
- {
- List<Dictionary<string, object>> listDict = new List<Dictionary<string, object>>();
- foreach (XElement node in nodes)
- {
- if (HTEXLib.ShapeHelper.GetPPTXNodeByPath(node, "c:xVal") != null)
- {
- Dictionary<string, object> dict = new Dictionary<string, object>();
- var xCvNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(node, "c:xVal/c:numRef/c:numCache/c:pt/c:v");
- if (xCvNodes != null)
- {
- List<string> list = new List<string>();
- foreach (XElement cvNode in xCvNodes)
- {
- list.Add(cvNode.Value);
- }
- dict.Add("xAxis", list);
- }
- var yCvNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(node, "c:yVal/c:numRef/c:numCache/c:pt/c:v");
- if (yCvNodes != null)
- {
- List<string> list = new List<string>();
- foreach (XElement cvNode in yCvNodes)
- {
- list.Add(cvNode.Value);
- }
- dict.Add("yAxis", list);
- }
- dict.Add("colName", "-");
- listDict.Add(dict);
- }
- else
- {
- Dictionary<string, object> dict = new Dictionary<string, object>();
- var spPr = HTEXLib.ShapeHelper.GetPPTXNodeByPath(node, "c:spPr");
- if (spPr != null)
- {
- var ChartShapeProperties = new ChartShapeProperties(spPr.ToString());
- HTEXLib.Models.HTEX. ShapeStyle shapeStyle = WORDHelper.DoShapeProperties(wordDoc,ChartShapeProperties,element, ImageHandler, positionc);
- dict.Add("colStyle", shapeStyle);
- }
- else
- {
- dict.Add("colStyle", null);
- }
- var colNameNode = HTEXLib.ShapeHelper.GetPPTXNodeByPath(node,"c:tx/c:strRef/c:strCache/c:pt/c:v");
- if (colNameNode != null)
- {
- dict.Add("colName", colNameNode.Value);
- }
- //name
- var catNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(node, "c:cat/c:strRef/c:strCache/c:pt/c:v");
- if (catNodes == null)
- {
- catNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(node, "c:cat/c:numRef/c:numCache/c:pt/c:v");
- }
- if (catNodes != null)
- {
- List<string> list = new List<string>();
- foreach (XElement cvNode in catNodes)
- {
- list.Add(cvNode.Value);
- }
- dict.Add("xAxis", list);
- }
- //value
- var valNodes = HTEXLib.ShapeHelper.GetPPTXNodeListByPath(node, "c:val/c:numRef/c:numCache/c:pt/c:v");
- if (valNodes != null)
- {
- List<string> list = new List<string>();
- foreach (XElement cvNode in valNodes)
- {
- list.Add(cvNode.Value);
- }
- dict.Add("yAxis", list);
- }
- listDict.Add(dict);
- }
- }
- return listDict;
- }
- return null;
- }
- public static XElement ProcessDgm(WordprocessingDocument wordDoc, XElement containerElement, Func<ImageInfo, XElement> imageHandler,
- int? extentCx, int? extentCy, string altText, XElement element, string hyperlinkUri)
- {
- var rdm = element.Attribute(R.dm);
- var dgm = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp => pp.RelationshipId == rdm.Value);
- return null;
- }
- public static string ProcessWps(WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> imageHandler) {
- // var wps = element.Element(MC.Choice).Element(W.drawing).Element(WP.anchor).Element(A.graphic).Element(A.graphicData).Element(WPS.wsp);
- WordprocessingShape wsp = new WordprocessingShape(element.ToString());
- var ext = element.GetWordNodeByPath("wps:spPr/a:xfrm/a:ext");
- var cx = ext.Attribute("cx").Value;
- var cy = ext.Attribute("cy").Value;
- HTEXLib.Shape shape = new HTEXLib.Shape();
- HTEXLib.Position position = new HTEXLib.Position { x = 0, y = 0, cx = double.Parse(cx) * 96.0 / 914400, cy = double.Parse(cy) * 96.0 / 914400 };
- shape.style.position = position;
- var ShapeStyle = WORDHelper.DoShapeProperties(wordDoc, wsp.GetFirstChild<DocumentFormat.OpenXml.Office2010.Word.DrawingShape.ShapeProperties>(), element, imageHandler, position);
- SlideColor slideColor = WORDHelper.DoShapeStyle(wsp.GetFirstChild<DocumentFormat.OpenXml.Office2010.Word.DrawingShape.ShapeStyle>(), wordDoc);
- if (ShapeStyle.border != null) {
- shape.style.border = ShapeStyle.border;
- }
- //从ShapeProperties 获取 p:spPr
- //再从 ShapeStyle 获取 p:spPr
- if (ShapeStyle.border == null || ShapeStyle.border.color == null || ShapeStyle.border.color.type == -1)
- {
- if (slideColor != null)
- {
- shape.style.border.color.solidFill = slideColor.LineColor;
- shape.style.border.color.type = 2;
- shape.style.border.outline = ShapeStyle.border != null && ShapeStyle.border.outline != null ? ShapeStyle.border.outline : null;
- }
- }
- else
- {
- shape.style.border = ShapeStyle.border;
- shape.style.border.outline = ShapeStyle.border != null && ShapeStyle.border.outline != null ? ShapeStyle.border.outline : ShapeStyle.border.outline != null? ShapeStyle.border.outline:null;
- }
- if (ShapeStyle.fill == null || ShapeStyle.fill.type == -1)
- {
- if (slideColor != null)
- {
- shape.style.fill.solidFill = slideColor.FillColor;
- shape.style.fill.type = 2;
- }
- }
- else
- {
- shape.style.fill = ShapeStyle.fill;
- }
- var wpars = element.GetWordNodeListByPath("wps:txbx/w:txbxContent/w:p");
- List<string> pars = new List<string>();
- if (wpars != null)
- {
- foreach (var wpar in wpars)
- {
- if (wpar.Value != null)
- {
- pars.Add(wpar.Value);
- }
- }
- }
- var prstGeom = element.GetWordNodeByPath("wps:spPr/a:prstGeom");
- if (prstGeom != null)
- {
- var shapeType = prstGeom.Attribute("prst").Value;
- var svg = SvgHelper.GenShapeSvg(new PresetGeometry(prstGeom.ToString()), shapeType, position, null);
- shape.svg = svg;
- }
- else
- {
- var custGeom = element.GetWordNodeByPath("wps:spPr/a:custGeom");
- if (custGeom != null)
- {
- DocumentFormat.OpenXml.Drawing.CustomGeometry shapeTypeCustom = new CustomGeometry(custGeom.ToString());
- shape = PPTXHelper.DoCustomGeometry(shapeTypeCustom, shape);
- }
- }
- return DrawingShape(shape);
- }
- public static string DrawingShape(HTEXLib.Shape shape) {
-
- StringBuilder builder = new StringBuilder();
- if (shape.svg != null)
- {
- builder.Append($"<svg xmlns='http://www.w3.org/2000/svg' style='width:{shape.style.position.cx}px;height:{shape.style.position.cy}px;rotate({shape.style.position.rot}deg); overflow: visible;'>");
- string bdc = shape.style.border != null && shape.style.border.color != null && shape.style.border.color.solidFill != null ? shape.style.border.color.solidFill : "none";
- string bds = shape.style.border != null && shape.style.border.stroke != null ? shape.style.border.stroke : "0";
- double? bdw = shape.style.border != null && shape.style.border.outline != null && shape.style.border.outline.width != null ? shape.style.border.outline.width : 0;
- string bgc = shape.style.fill != null && shape.style.fill != null && shape.style.fill.solidFill != null ? shape.style.fill.solidFill : "none";
- if (shape.style.border!=null&&(shape.style.border.tailEnd != null || shape.style.border.headEnd != null)) {
- builder.Append("<defs xmlns='http://www.w3.org/2000/svg'>");
- builder.Append($"<marker id='marker{shape.uid}' viewBox='0 0 20 20' refX='1' refY='10' markerWidth='10' markerHeight='20' stroke='{bdc}' fill='{bdc}' orient='auto-start-reverse' markerUnits='strokeWidth'><path d='M 0 0 L 20 10 L 0 20 z' /></marker>");
- builder.Append("</defs>");
- }
- string stm = "";
- string edm = "";
- if (shape.style.border!=null&&shape.style.border.headEnd != null) {
- stm= $" marker-start='url(#marker{ shape.uid})'" ;
- }
- if (shape.style.border != null && shape.style.border.tailEnd != null)
- {
- edm = $" marker-end='url(#marker{ shape.uid})'";
- }
- builder.Append($"<path xmlns='http://www.w3.org/2000/svg' d='{shape.svg.d}' fill='{bgc}' stroke='{bdc}' stroke-width='{bdw}' stroke-dasharray='{bds}' {stm} {edm} />");
- builder.Append("</svg>");
- }
- return builder.ToString() ;
- }
- public static XElement ProcessOMath(XElement element)
- {
- //XslCompiledTransform xslTransform = new XslCompiledTransform();
- // xslTransform.Load(BaseConfigModel.ContentRootPath + "/Config/Core/OMML2MML.XSL");
- XmlReader OMML2MML = XmlReader.Create(new StringReader(Globals.OMML2MML));
- XslCompiledTransform xslTransform = new XslCompiledTransform();
- xslTransform.Load(OMML2MML);
- string mathXml = element.ToString();
- string officeML = string.Empty;
- using (TextReader tr = new StringReader(mathXml))
- {
- using (XmlReader reader = XmlReader.Create(tr))
- {
- using (MemoryStream ms = new MemoryStream())
- {
- XmlWriterSettings settings = xslTransform.OutputSettings.Clone();
- settings.ConformanceLevel = ConformanceLevel.Fragment;
- settings.OmitXmlDeclaration = true;
- XmlWriter xw = XmlWriter.Create(ms, settings);
- xslTransform.Transform(reader, xw);
- ms.Seek(0, SeekOrigin.Begin);
- using (StreamReader sr = new StreamReader(ms, Encoding.UTF8))
- {
- officeML = sr.ReadToEnd();
- // Console.Out.WriteLine(officeML);
- }
- }
- }
- }
- officeML= officeML.Replace("mml:", "");
- XElement officeElement = XElement.Load(new StringReader(officeML));
- return officeElement;
- }
- private static object ProcessHyperlinkToBookmark(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element)
- {
- var style = new Dictionary<string, string>();
- var a = new XElement(Xhtml.a,
- new XAttribute("href", "#" + (string)element.Attribute(W.anchor)),
- element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run)));
- if (!a.Nodes().Any())
- a.Add(new XText(""));
- style.Add("text-decoration", "none");
- a.AddAnnotation(style);
- return a;
- }
- private static object ProcessBookmarkStart(XElement element)
- {
- var name = (string)element.Attribute(W.name);
- if (name == null) return null;
- var style = new Dictionary<string, string>();
- var a = new XElement(Xhtml.a,
- new XAttribute("id", name),
- new XText(""));
- if (!a.Nodes().Any())
- a.Add(new XText(""));
- style.Add("text-decoration", "none");
- a.AddAnnotation(style);
- return a;
- }
- private static object ProcessTab(XElement element)
- {
- var tabWidthAtt = element.Attribute(PtOpenXml.TabWidth);
- if (tabWidthAtt == null) return null;
- var leader = (string)element.Attribute(PtOpenXml.Leader);
- var tabWidth = (decimal)tabWidthAtt;
- var style = new Dictionary<string, string>();
- XElement span;
- if (leader != null)
- {
- var leaderChar = ".";
- if (leader == "hyphen")
- leaderChar = "-";
- else if (leader == "dot")
- leaderChar = ".";
- else if (leader == "underscore")
- leaderChar = "_";
- var runContainingTabToReplace = element.Ancestors(W.r).First();
- var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName") ??
- runContainingTabToReplace.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName");
- var dummyRun = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, leaderChar));
- var widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun);
- bool forceArial = false;
- if (widthOfLeaderChar == 0)
- {
- dummyRun = new XElement(W.r,
- new XAttribute(PtOpenXml.FontName, "Arial"),
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, leaderChar));
- widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun);
- forceArial = true;
- }
- if (widthOfLeaderChar != 0)
- {
- var numberOfLeaderChars = (int)(Math.Floor((tabWidth * 1440) / widthOfLeaderChar));
- if (numberOfLeaderChars < 0)
- numberOfLeaderChars = 0;
- span = new XElement(Xhtml.span,
- new XAttribute(XNamespace.Xml + "space", "preserve"),
- " " + "".PadRight(numberOfLeaderChars, leaderChar[0]) + " ");
- style.Add("margin", "0 0 0 0");
- style.Add("padding", "0 0 0 0");
- style.Add("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth));
- style.Add("text-align", "center");
- if (forceArial)
- style.Add("font-family", "Arial");
- }
- else
- {
- span = new XElement(Xhtml.span, new XAttribute(XNamespace.Xml + "space", "preserve"), " ");
- style.Add("margin", "0 0 0 0");
- style.Add("padding", "0 0 0 0");
- style.Add("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth));
- style.Add("text-align", "center");
- if (leader == "underscore")
- {
- style.Add("text-decoration", "underline");
- }
- }
- }
- else
- {
- #if false
- var bidi = element
- .Ancestors(W.p)
- .Take(1)
- .Elements(W.pPr)
- .Elements(W.bidi)
- .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true)
- .FirstOrDefault();
- var isBidi = bidi != null;
- if (isBidi)
- span = new XElement(Xhtml.span, new XEntity("#x200f")); // RLM
- else
- span = new XElement(Xhtml.span, new XEntity("#x200e")); // LRM
- #else
- span = new XElement(Xhtml.span, new XEntity("#x00a0"));
- #endif
- style.Add("margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.00}in", tabWidth));
- style.Add("padding", "0 0 0 0");
- }
- span.AddAnnotation(style);
- return span;
- }
- private static object ProcessBreak(XElement element)
- {
- XElement span = null;
- var tabWidth = (decimal?)element.Attribute(PtOpenXml.TabWidth);
- if (tabWidth != null)
- {
- span = new XElement(Xhtml.span);
- span.AddAnnotation(new Dictionary<string, string>
- {
- { "margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.00}in", tabWidth) },
- { "padding", "0 0 0 0" }
- });
- }
- var paragraph = element.Ancestors(W.p).FirstOrDefault();
- var isBidi = paragraph != null &&
- paragraph.Elements(W.pPr).Elements(W.bidi).Any(b => b.Attribute(W.val) == null ||
- b.Attribute(W.val).ToBoolean() == true);
- var zeroWidthChar = isBidi ? new XEntity("#x200f") : new XEntity("#x200e");
- return new object[]
- {
- new XElement(Xhtml.br),
- zeroWidthChar,
- span,
- };
- }
- private static object ProcessContentControl(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- XElement element, decimal currentMarginLeft)
- {
- var relevantAncestors = element.Ancestors().TakeWhile(a => a.Name != W.txbxContent);
- var isRunLevelContentControl = relevantAncestors.Any(a => a.Name == W.p);
- if (isRunLevelContentControl)
- {
- return element.Elements(W.sdtContent).Elements()
- .Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft))
- .ToList();
- }
- return CreateBorderDivs(wordDoc, settings, element.Elements(W.sdtContent).Elements());
- }
- // Transform the w:p element, including the following sibling w:p element(s)
- // in case the w:p element has a style separator. The sibling(s) will be
- // transformed to h:span elements rather than h:p elements and added to
- // the element (e.g., h:h2) created from the w:p element having the (first)
- // style separator (i.e., a w:specVanish element).
- private static object ProcessParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- XElement element, bool suppressTrailingWhiteSpace, decimal currentMarginLeft)
- {
- // Ignore this paragraph if the previous paragraph has a style separator.
- // We have already transformed this one together with the previous one.
- var previousParagraph = element.ElementsBeforeSelf(W.p).LastOrDefault();
- if (HasStyleSeparator(previousParagraph)) return null;
- var elementName = GetParagraphElementName(element, wordDoc);
- var isBidi = IsBidi(element);
- var paragraph = (XElement)ConvertParagraph(wordDoc, settings, element, elementName,
- suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
- // The paragraph conversion might have created empty spans.
- // These can and should be removed because empty spans are
- // invalid in HTML5.
- paragraph.Elements(Xhtml.span).Where(e => e.IsEmpty).Remove();
- foreach (var span in paragraph.Elements(Xhtml.span).ToList())
- {
- var v = span.Value;
- if (v.Length > 0 && (char.IsWhiteSpace(v[0]) || char.IsWhiteSpace(v[v.Length - 1])) && span.Attribute(XNamespace.Xml + "space") == null)
- span.Add(new XAttribute(XNamespace.Xml + "space", "preserve"));
- }
- while (HasStyleSeparator(element))
- {
- element = element.ElementsAfterSelf(W.p).FirstOrDefault();
- if (element == null) break;
- elementName = Xhtml.span;
- isBidi = IsBidi(element);
- var span = (XElement)ConvertParagraph(wordDoc, settings, element, elementName,
- suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
- var v = span.Value;
- if (v.Length > 0 && (char.IsWhiteSpace(v[0]) || char.IsWhiteSpace(v[v.Length - 1])) && span.Attribute(XNamespace.Xml + "space") == null)
- span.Add(new XAttribute(XNamespace.Xml + "space", "preserve"));
- paragraph.Add(span);
- }
- return paragraph;
- }
- private static object ProcessTable(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element, decimal currentMarginLeft)
- {
- var style = new Dictionary<string, string>();
- style.AddIfMissing("border-collapse", "collapse");
- style.AddIfMissing("border", "none");
- var bidiVisual = element.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
- var tblW = element.Elements(W.tblPr).Elements(W.tblW).FirstOrDefault();
- if (tblW != null)
- {
- var type = (string)tblW.Attribute(W.type);
- if (type != null && type == "pct")
- {
- var w = (int)tblW.Attribute(W._w);
- style.AddIfMissing("width", (w / 50) + "%");
- }
- }
- var tblInd = element.Elements(W.tblPr).Elements(W.tblInd).FirstOrDefault();
- if (tblInd != null)
- {
- var tblIndType = (string)tblInd.Attribute(W.type);
- if (tblIndType != null)
- {
- if (tblIndType == "dxa")
- {
- var width = (decimal?)tblInd.Attribute(W._w);
- if (width != null)
- {
- style.AddIfMissing("margin-left",
- width > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m)
- : "0");
- }
- }
- }
- }
- var tableDirection = bidiVisual != null ? new XAttribute("dir", "rtl") : new XAttribute("dir", "ltr");
- style.AddIfMissing("margin-bottom", ".001pt");
- var table = new XElement(Xhtml.table,
- // TODO: Revisit and make sure the omission is covered by appropriate CSS.
- // new XAttribute("border", "1"),
- // new XAttribute("cellspacing", 0),
- // new XAttribute("cellpadding", 0),
- tableDirection,
- element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
- table.AddAnnotation(style);
- var jc = (string)element.Elements(W.tblPr).Elements(W.jc).Attributes(W.val).FirstOrDefault() ?? "left";
- XAttribute dir = null;
- XAttribute jcToUse = null;
- if (bidiVisual != null)
- {
- dir = new XAttribute("dir", "rtl");
- if (jc == "left")
- jcToUse = new XAttribute("align", "right");
- else if (jc == "right")
- jcToUse = new XAttribute("align", "left");
- else if (jc == "center")
- jcToUse = new XAttribute("align", "center");
- }
- else
- {
- jcToUse = new XAttribute("align", jc);
- }
- var tableDiv = new XElement(Xhtml.div,
- dir,
- jcToUse,
- table);
- return tableDiv;
- }
- [SuppressMessage("ReSharper", "PossibleNullReferenceException")]
- private static object ProcessTableCell(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element)
- {
- var style = new Dictionary<string, string>();
- XAttribute colSpan = null;
- XAttribute rowSpan = null;
- var tcPr = element.Element(W.tcPr);
- if (tcPr != null)
- {
- if ((string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
- {
- var currentRow = element.Parent.ElementsBeforeSelf(W.tr).Count();
- var currentCell = element.ElementsBeforeSelf(W.tc).Count();
- var tbl = element.Parent.Parent;
- int rowSpanCount = 1;
- currentRow += 1;
- while (true)
- {
- var row = tbl.Elements(W.tr).Skip(currentRow).FirstOrDefault();
- if (row == null)
- break;
- var cell2 = row.Elements(W.tc).Skip(currentCell).FirstOrDefault();
- if (cell2 == null)
- break;
- if (cell2.Elements(W.tcPr).Elements(W.vMerge).FirstOrDefault() == null)
- break;
- if ((string)cell2.Elements(W.tcPr).Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
- break;
- currentRow += 1;
- rowSpanCount += 1;
- }
- rowSpan = new XAttribute("rowspan", rowSpanCount);
- }
- if (tcPr.Element(W.vMerge) != null &&
- (string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() != "restart")
- return null;
- if (tcPr.Element(W.vAlign) != null)
- {
- var vAlignVal = (string)tcPr.Elements(W.vAlign).Attributes(W.val).FirstOrDefault();
- if (vAlignVal == "00")
- style.AddIfMissing("vertical-align", "top");
- else if (vAlignVal == "center")
- style.AddIfMissing("vertical-align", "middle");
- else if (vAlignVal == "bottom")
- style.AddIfMissing("vertical-align", "bottom");
- else
- style.AddIfMissing("vertical-align", "middle");
- }
- style.AddIfMissing("vertical-align", "top");
- if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "dxa")
- {
- decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
- style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m));
- }
- if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "pct")
- {
- decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
- style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", width / 50m));
- }
- var tcBorders = tcPr.Element(W.tcBorders);
- GenerateBorderStyle(tcBorders, W.top, style, BorderType.Cell);
- GenerateBorderStyle(tcBorders, W.right, style, BorderType.Cell);
- GenerateBorderStyle(tcBorders, W.bottom, style, BorderType.Cell);
- GenerateBorderStyle(tcBorders, W.left, style, BorderType.Cell);
- CreateStyleFromShd(style, tcPr.Element(W.shd));
- var gridSpan = tcPr.Elements(W.gridSpan).Attributes(W.val).Select(a => (int?)a).FirstOrDefault();
- if (gridSpan != null)
- colSpan = new XAttribute("colspan", (int)gridSpan);
- }
- style.AddIfMissing("padding-top", "0");
- style.AddIfMissing("padding-bottom", "0");
- var cell = new XElement(Xhtml.td,
- rowSpan,
- colSpan,
- CreateBorderDivs(wordDoc, settings, element.Elements()));
- cell.AddAnnotation(style);
- return cell;
- }
- private static object ProcessTableRow(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element,
- decimal currentMarginLeft)
- {
- var style = new Dictionary<string, string>();
- int? trHeight = (int?)element.Elements(W.trPr).Elements(W.trHeight).Attributes(W.val).FirstOrDefault();
- if (trHeight != null)
- style.AddIfMissing("height",
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", (decimal)trHeight / 1440m));
- var htmlRow = new XElement(Xhtml.tr,
- element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
- if (style.Any())
- htmlRow.AddAnnotation(style);
- return htmlRow;
- }
- private static bool HasStyleSeparator(XElement element)
- {
- return element != null && element.Elements(W.pPr).Elements(W.rPr).Any(e => GetBoolProp(e, W.specVanish));
- }
- private static bool IsBidi(XElement element)
- {
- return element
- .Elements(W.pPr)
- .Elements(W.bidi)
- .Any(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true);
- }
- private static XName GetParagraphElementName(XElement element, WordprocessingDocument wordDoc)
- {
- var elementName = Xhtml.p;
- var styleId = (string)element.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
- if (styleId == null) return elementName;
- var style = GetStyle(styleId, wordDoc);
- if (style == null) return elementName;
- var outlineLevel =
- (int?)style.Elements(W.pPr).Elements(W.outlineLvl).Attributes(W.val).FirstOrDefault();
- if (outlineLevel != null && outlineLevel <= 5)
- {
- elementName = Xhtml.xhtml + string.Format("h{0}", outlineLevel + 1);
- }
- return elementName;
- }
- private static XElement GetStyle(string styleId, WordprocessingDocument wordDoc)
- {
- var stylesPart = wordDoc.MainDocumentPart.StyleDefinitionsPart;
- if (stylesPart == null) return null;
- var styles = stylesPart.GetXDocument().Root;
- return styles != null
- ? styles.Elements(W.style).FirstOrDefault(s => (string)s.Attribute(W.styleId) == styleId)
- : null;
- }
- private static object CreateSectionDivs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element)
- {
- // note: when building a paging html converter, need to attend to new sections with page breaks here.
- // This code conflates adjacent sections if they have identical formatting, which is not an issue
- // for the non-paging transform.
- var groupedIntoDivs = element
- .Elements()
- .GroupAdjacent(e =>
- {
- var sectAnnotation = e.Annotation<SectionAnnotation>();
- return sectAnnotation != null ? sectAnnotation.SectionElement.ToString() : "";
- });
- // note: when creating a paging html converter, need to pay attention to w:rtlGutter element.
- var divList = groupedIntoDivs
- .Select(g =>
- {
- var sectPr = g.First().Annotation<SectionAnnotation>();
- XElement bidi = null;
- if (sectPr != null)
- {
- bidi = sectPr
- .SectionElement
- .Elements(W.bidi)
- .FirstOrDefault(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true);
- }
- if (sectPr == null || bidi == null)
- {
- var div = new XElement(Xhtml.div, CreateBorderDivs(wordDoc, settings, g));
- return div;
- }
- else
- {
- var div = new XElement(Xhtml.div,
- new XAttribute("dir", "rtl"),
- CreateBorderDivs(wordDoc, settings, g));
- return div;
- }
- });
- return divList;
- }
- private enum BorderType
- {
- Paragraph,
- Cell,
- };
- /*
- * Notes on line spacing
- *
- * the w:line and w:lineRule attributes control spacing between lines - including between lines within a paragraph
- *
- * If w:spacing w:lineRule="auto" then
- * w:spacing w:line is a percentage where 240 == 100%
- *
- * (line value / 240) * 100 = percentage of line
- *
- * If w:spacing w:lineRule="exact" or w:lineRule="atLeast" then
- * w:spacing w:line is in twips
- * 1440 = exactly one inch from line to line
- *
- * Handle
- * - ind
- * - jc
- * - numPr
- * - pBdr
- * - shd
- * - spacing
- * - textAlignment
- *
- * Don't Handle (yet)
- * - adjustRightInd?
- * - autoSpaceDE
- * - autoSpaceDN
- * - bidi
- * - contextualSpacing
- * - divId
- * - framePr
- * - keepLines
- * - keepNext
- * - kinsoku
- * - mirrorIndents
- * - overflowPunct
- * - pageBreakBefore
- * - snapToGrid
- * - suppressAutoHyphens
- * - suppressLineNumbers
- * - suppressOverlap
- * - tabs
- * - textBoxTightWrap
- * - textDirection
- * - topLinePunct
- * - widowControl
- * - wordWrap
- *
- */
- private static object ConvertParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- XElement paragraph, XName elementName, bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi)
- {
- var style = DefineParagraphStyle(paragraph, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
- var rtl = isBidi ? new XAttribute("dir", "rtl") : new XAttribute("dir", "ltr");
- var firstMark = isBidi ? new XEntity("#x200f") : null;
- // Analyze initial runs to see whether we have a tab, in which case we will render
- // a span with a defined width and ignore the tab rather than rendering the text
- // preceding the tab and the tab as a span with a computed width.
- var firstTabRun = paragraph
- .Elements(W.r)
- .FirstOrDefault(run => run.Elements(W.tab).Any());
- var elementsPrecedingTab = firstTabRun != null
- ? paragraph.Elements(W.r).TakeWhile(e => e != firstTabRun)
- .Where(e => e.Elements().Any(c => c.Attributes(PtOpenXml.TabWidth).Any())).ToList()
- : Enumerable.Empty<XElement>().ToList();
- // TODO: Revisit
- // For the time being, if a hyperlink field precedes the tab, we'll render it as before.
- var hyperlinkPrecedesTab = elementsPrecedingTab
- .Elements(W.r)
- .Elements(W.instrText)
- .Select(e => e.Value)
- .Any(value => value != null && value.TrimStart().ToUpper().StartsWith("HYPERLINK"));
- if (hyperlinkPrecedesTab)
- {
- var paraElement1 = new XElement(elementName,
- rtl,
- firstMark,
- ConvertContentThatCanContainFields(wordDoc, settings, paragraph.Elements()));
- paraElement1.AddAnnotation(style);
- return paraElement1;
- }
- var txElementsPrecedingTab = TransformElementsPrecedingTab(wordDoc, settings, elementsPrecedingTab, firstTabRun);
- var elementsSucceedingTab = firstTabRun != null
- ? paragraph.Elements().SkipWhile(e => e != firstTabRun).Skip(1)
- : paragraph.Elements();
- var paraElement = new XElement(elementName,
- rtl,
- firstMark,
- txElementsPrecedingTab,
- ConvertContentThatCanContainFields(wordDoc, settings, elementsSucceedingTab));
- paraElement.AddAnnotation(style);
- return paraElement;
- }
- private static List<object> TransformElementsPrecedingTab(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- List<XElement> elementsPrecedingTab, XElement firstTabRun)
- {
- var tabWidth = firstTabRun != null
- ? (decimal?)firstTabRun.Elements(W.tab).Attributes(PtOpenXml.TabWidth).FirstOrDefault() ?? 0m
- : 0m;
- var precedingElementsWidth = elementsPrecedingTab
- .Elements()
- .Where(c => c.Attributes(PtOpenXml.TabWidth).Any())
- .Select(e => (decimal)e.Attribute(PtOpenXml.TabWidth))
- .Sum();
- var totalWidth = precedingElementsWidth + tabWidth;
- var txElementsPrecedingTab = elementsPrecedingTab
- .Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m))
- .ToList();
- if (txElementsPrecedingTab.Count > 1)
- {
- var span = new XElement(Xhtml.span, txElementsPrecedingTab);
- var spanStyle = new Dictionary<string, string>
- {
- { "display", "inline-block" },
- { "text-indent", "0" },
- { "width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth) }
- };
- span.AddAnnotation(spanStyle);
- }
- else if (txElementsPrecedingTab.Count == 1)
- {
- var element = txElementsPrecedingTab.First() as XElement;
- if (element != null)
- {
- var spanStyle = element.Annotation<Dictionary<string, string>>();
- spanStyle.AddIfMissing("display", "inline-block");
- spanStyle.AddIfMissing("text-indent", "0");
- spanStyle.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth));
- }
- }
- return txElementsPrecedingTab;
- }
- private static Dictionary<string, string> DefineParagraphStyle(XElement paragraph, XName elementName,
- bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi)
- {
- var style = new Dictionary<string, string>();
- var styleName = (string)paragraph.Attribute(PtOpenXml.StyleName);
- if (styleName != null)
- style.Add("PtStyleName", styleName);
- var pPr = paragraph.Element(W.pPr);
- if (pPr == null) return style;
- CreateStyleFromSpacing(style, pPr.Element(W.spacing), elementName, suppressTrailingWhiteSpace);
- CreateStyleFromInd(style, pPr.Element(W.ind), elementName, currentMarginLeft, isBidi);
- // todo need to handle
- // - both
- // - mediumKashida
- // - distribute
- // - numTab
- // - highKashida
- // - lowKashida
- // - thaiDistribute
- CreateStyleFromJc(style, pPr.Element(W.jc), isBidi);
- CreateStyleFromShd(style, pPr.Element(W.shd));
- // Pt.FontName
- var font = (string)paragraph.Attributes(PtOpenXml.FontName).FirstOrDefault();
- if (font != null)
- CreateFontCssProperty(font, style);
- DefineFontSize(style, paragraph);
- DefineLineHeight(style, paragraph);
- // vertical text alignment as of December 2013 does not work in any major browsers.
- CreateStyleFromTextAlignment(style, pPr.Element(W.textAlignment));
- style.AddIfMissing("margin-top", "0");
- style.AddIfMissing("margin-left", "0");
- style.AddIfMissing("margin-right", "0");
- style.AddIfMissing("margin-bottom", ".001pt");
- return style;
- }
- private static void CreateStyleFromInd(Dictionary<string, string> style, XElement ind, XName elementName,
- decimal currentMarginLeft, bool isBidi)
- {
- if (ind == null) return;
- var left = (decimal?)ind.Attribute(W.left);
- if (left != null && elementName != Xhtml.span)
- {
- var leftInInches = (decimal)left / 1440 - currentMarginLeft;
- style.AddIfMissing(isBidi ? "margin-right" : "margin-left",
- leftInInches > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", leftInInches)
- : "0");
- }
- var right = (decimal?)ind.Attribute(W.right);
- if (right != null)
- {
- var rightInInches = (decimal)right / 1440;
- style.AddIfMissing(isBidi ? "margin-left" : "margin-right",
- rightInInches > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", rightInInches)
- : "0");
- }
- var firstLine = (decimal?)ind.Attribute(W.firstLine);
- if (firstLine != null && elementName != Xhtml.span)
- {
- var firstLineInInches = (decimal)firstLine / 1440m;
- style.AddIfMissing("text-indent",
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", firstLineInInches));
- }
- var hanging = (decimal?)ind.Attribute(W.hanging);
- if (hanging != null && elementName != Xhtml.span)
- {
- var hangingInInches = (decimal)-hanging / 1440m;
- style.AddIfMissing("text-indent",
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", hangingInInches));
- }
- }
- private static void CreateStyleFromJc(Dictionary<string, string> style, XElement jc, bool isBidi)
- {
- if (jc != null)
- {
- var jcVal = (string)jc.Attributes(W.val).FirstOrDefault() ?? "left";
- if (jcVal == "left")
- style.AddIfMissing("text-align", isBidi ? "right" : "left");
- else if (jcVal == "right")
- style.AddIfMissing("text-align", isBidi ? "left" : "right");
- else if (jcVal == "center")
- style.AddIfMissing("text-align", "center");
- else if (jcVal == "both")
- style.AddIfMissing("text-align", "justify");
- }
- }
- private static void CreateStyleFromSpacing(Dictionary<string, string> style, XElement spacing, XName elementName,
- bool suppressTrailingWhiteSpace)
- {
- if (spacing == null) return;
- var spacingBefore = (decimal?)spacing.Attribute(W.before);
- if (spacingBefore != null && elementName != Xhtml.span)
- style.AddIfMissing("margin-top",
- spacingBefore > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingBefore / 20.0m)
- : "0");
- var lineRule = (string)spacing.Attribute(W.lineRule);
- if (lineRule == "auto")
- {
- var line = (decimal)spacing.Attribute(W.line);
- if (line != 240m)
- {
- var pct = (line / 240m) * 100m;
- style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", pct));
- }
- }
- if (lineRule == "exact")
- {
- var line = (decimal)spacing.Attribute(W.line);
- var points = line / 20m;
- style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points));
- }
- if (lineRule == "atLeast")
- {
- var line = (decimal)spacing.Attribute(W.line);
- var points = line / 20m;
- if (points >= 14m)
- style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points));
- }
- var spacingAfter = suppressTrailingWhiteSpace ? 0m : (decimal?)spacing.Attribute(W.after);
- if (spacingAfter != null)
- style.AddIfMissing("margin-bottom",
- spacingAfter > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingAfter / 20.0m)
- : "0");
- }
- private static void CreateStyleFromTextAlignment(Dictionary<string, string> style, XElement textAlignment)
- {
- if (textAlignment == null) return;
- var verticalTextAlignment = (string)textAlignment.Attributes(W.val).FirstOrDefault();
- if (verticalTextAlignment == null || verticalTextAlignment == "auto") return;
- if (verticalTextAlignment == "top")
- style.AddIfMissing("vertical-align", "top");
- else if (verticalTextAlignment == "center")
- style.AddIfMissing("vertical-align", "middle");
- else if (verticalTextAlignment == "baseline")
- style.AddIfMissing("vertical-align", "baseline");
- else if (verticalTextAlignment == "bottom")
- style.AddIfMissing("vertical-align", "bottom");
- }
- private static void DefineFontSize(Dictionary<string, string> style, XElement paragraph)
- {
- var sz = paragraph
- .DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.r)
- .Select(r => GetFontSize(r))
- .Max();
- if (sz != null)
- style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz / 2.0m));
- }
- private static void DefineLineHeight(Dictionary<string, string> style, XElement paragraph)
- {
- var allRunsAreUniDirectional = paragraph
- .DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.r)
- .Select(run => (string)run.Attribute(PtOpenXml.LanguageType))
- .All(lt => lt != "bidi");
- if (allRunsAreUniDirectional)
- style.AddIfMissing("line-height", "108%");
- }
- /*
- * Handle:
- * - b
- * - bdr
- * - caps
- * - color
- * - dstrike
- * - highlight
- * - i
- * - position
- * - rFonts
- * - shd
- * - smallCaps
- * - spacing
- * - strike
- * - sz
- * - u
- * - vanish
- * - vertAlign
- *
- * Don't handle:
- * - em
- * - emboss
- * - fitText
- * - imprint
- * - kern
- * - outline
- * - shadow
- * - w
- *
- */
- private static object ConvertRun(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement run)
- {
- var rPr = run.Element(W.rPr);
- if (rPr == null)
- return run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m));
- // hide all content that contains the w:rPr/w:webHidden element
- if (rPr.Element(W.webHidden) != null)
- return null;
- var style = DefineRunStyle(run);
- object content = run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m));
- // Wrap content in h:sup or h:sub elements as necessary.
- if (rPr.Element(W.vertAlign) != null)
- {
- XElement newContent = null;
- var vertAlignVal = (string)rPr.Elements(W.vertAlign).Attributes(W.val).FirstOrDefault();
- switch (vertAlignVal)
- {
- case "superscript":
- newContent = new XElement(Xhtml.sup, content);
- break;
- case "subscript":
- newContent = new XElement(Xhtml.sub, content);
- break;
- }
- if (newContent != null && newContent.Nodes().Any())
- content = newContent;
- }
- var langAttribute = GetLangAttribute(run);
- XEntity runStartMark;
- XEntity runEndMark;
- DetermineRunMarks(run, rPr, style, out runStartMark, out runEndMark);
- if (style.Any() || langAttribute != null || runStartMark != null)
- {
- style.AddIfMissing("margin", "0");
- style.AddIfMissing("padding", "0");
- var xe = new XElement(Xhtml.span,
- langAttribute,
- runStartMark,
- content,
- runEndMark);
- xe.AddAnnotation(style);
- content = xe;
- }
- return content;
- }
- [SuppressMessage("ReSharper", "FunctionComplexityOverflow")]
- private static Dictionary<string, string> DefineRunStyle(XElement run)
- {
- var style = new Dictionary<string, string>();
- var rPr = run.Elements(W.rPr).First();
- var styleName = (string)run.Attribute(PtOpenXml.StyleName);
- if (styleName != null)
- style.Add("PtStyleName", styleName);
- // W.bdr
- if (rPr.Element(W.bdr) != null && (string)rPr.Elements(W.bdr).Attributes(W.val).FirstOrDefault() != "none")
- {
- style.AddIfMissing("border", "solid windowtext 1.0pt");
- style.AddIfMissing("padding", "0");
- }
- // W.color
- var color = (string)rPr.Elements(W.color).Attributes(W.val).FirstOrDefault();
- if (color != null)
- CreateColorProperty("color", color, style);
- // W.highlight
- var highlight = (string)rPr.Elements(W.highlight).Attributes(W.val).FirstOrDefault();
- if (highlight != null)
- CreateColorProperty("background", highlight, style);
- // W.shd
- var shade = (string)rPr.Elements(W.shd).Attributes(W.fill).FirstOrDefault();
- if (shade != null)
- CreateColorProperty("background", shade, style);
- // Pt.FontName
- var sym = run.Element(W.sym);
- var font = sym != null
- ? (string)sym.Attributes(W.font).FirstOrDefault()
- : (string)run.Attributes(PtOpenXml.FontName).FirstOrDefault();
- if (font != null)
- CreateFontCssProperty(font, style);
- // W.sz
- var languageType = (string)run.Attribute(PtOpenXml.LanguageType);
- var sz = GetFontSize(languageType, rPr);
- if (sz != null)
- style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz / 2.0m));
- // W.caps
- if (GetBoolProp(rPr, W.caps))
- style.AddIfMissing("text-transform", "uppercase");
- // W.smallCaps
- if (GetBoolProp(rPr, W.smallCaps))
- style.AddIfMissing("font-variant", "small-caps");
- // W.spacing
- var spacingInTwips = (decimal?)rPr.Elements(W.spacing).Attributes(W.val).FirstOrDefault();
- if (spacingInTwips != null)
- style.AddIfMissing("letter-spacing",
- spacingInTwips > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingInTwips / 20)
- : "0");
- // W.position
- var position = (decimal?)rPr.Elements(W.position).Attributes(W.val).FirstOrDefault();
- if (position != null)
- {
- style.AddIfMissing("position", "relative");
- style.AddIfMissing("top", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", -(position / 2)));
- }
- // W.vanish
- if (GetBoolProp(rPr, W.vanish) && !GetBoolProp(rPr, W.specVanish))
- style.AddIfMissing("display", "none");
- // W.u
- if (rPr.Element(W.u) != null && (string)rPr.Elements(W.u).Attributes(W.val).FirstOrDefault() != "none")
- style.AddIfMissing("text-decoration", "underline");
- // W.i
- style.AddIfMissing("font-style", GetBoolProp(rPr, W.i) ? "italic" : "normal");
- // W.b
- style.AddIfMissing("font-weight", GetBoolProp(rPr, W.b) ? "bold" : "normal");
- // W.strike
- if (GetBoolProp(rPr, W.strike) || GetBoolProp(rPr, W.dstrike))
- style.AddIfMissing("text-decoration", "line-through");
- return style;
- }
- private static decimal? GetFontSize(XElement e)
- {
- var languageType = (string)e.Attribute(PtOpenXml.LanguageType);
- if (e.Name == W.p)
- {
- return GetFontSize(languageType, e.Elements(W.pPr).Elements(W.rPr).FirstOrDefault());
- }
- if (e.Name == W.r)
- {
- return GetFontSize(languageType, e.Element(W.rPr));
- }
- return null;
- }
- private static decimal? GetFontSize(string languageType, XElement rPr)
- {
- if (rPr == null) return null;
- return languageType == "bidi"
- ? (decimal?)rPr.Elements(W.szCs).Attributes(W.val).FirstOrDefault()
- : (decimal?)rPr.Elements(W.sz).Attributes(W.val).FirstOrDefault();
- }
- private static void DetermineRunMarks(XElement run, XElement rPr, Dictionary<string, string> style, out XEntity runStartMark, out XEntity runEndMark)
- {
- runStartMark = null;
- runEndMark = null;
- // Only do the following for text runs.
- if (run.Element(W.t) == null) return;
- // Can't add directional marks if the font-family is symbol - they are visible, and display as a ?
- var addDirectionalMarks = true;
- if (style.ContainsKey("font-family"))
- {
- if (style["font-family"].ToLower() == "symbol")
- addDirectionalMarks = false;
- }
- if (!addDirectionalMarks) return;
- var isRtl = rPr.Element(W.rtl) != null;
- if (isRtl)
- {
- runStartMark = new XEntity("#x200f"); // RLM
- runEndMark = new XEntity("#x200f"); // RLM
- }
- else
- {
- var paragraph = run.Ancestors(W.p).First();
- var paraIsBidi = paragraph
- .Elements(W.pPr)
- .Elements(W.bidi)
- .Any(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true);
- if (paraIsBidi)
- {
- runStartMark = new XEntity("#x200e"); // LRM
- runEndMark = new XEntity("#x200e"); // LRM
- }
- }
- }
- private static XAttribute GetLangAttribute(XElement run)
- {
- const string defaultLanguage = "en-US"; // todo need to get defaultLanguage
- var rPr = run.Elements(W.rPr).First();
- var languageType = (string)run.Attribute(PtOpenXml.LanguageType);
- string lang = null;
- if (languageType == "western")
- lang = (string)rPr.Elements(W.lang).Attributes(W.val).FirstOrDefault();
- else if (languageType == "bidi")
- lang = (string)rPr.Elements(W.lang).Attributes(W.bidi).FirstOrDefault();
- else if (languageType == "eastAsia")
- lang = (string)rPr.Elements(W.lang).Attributes(W.eastAsia).FirstOrDefault();
- if (lang == null)
- lang = defaultLanguage;
- return lang != defaultLanguage ? new XAttribute("lang", lang) : null;
- }
- private static void AdjustTableBorders(WordprocessingDocument wordDoc)
- {
- // Note: when implementing a paging version of the HTML transform, this needs to be done
- // for all content parts, not just the main document part.
- var xd = wordDoc.MainDocumentPart.GetXDocument();
- foreach (var tbl in xd.Descendants(W.tbl))
- AdjustTableBorders(tbl);
- wordDoc.MainDocumentPart.PutXDocument();
- }
- private static void AdjustTableBorders(XElement tbl)
- {
- var ta = tbl
- .Elements(W.tr)
- .Select(r => r
- .Elements(W.tc)
- .SelectMany(c =>
- Enumerable.Repeat(c,
- (int?)c.Elements(W.tcPr).Elements(W.gridSpan).Attributes(W.val).FirstOrDefault() ?? 1))
- .ToArray())
- .ToArray();
- for (var y = 0; y < ta.Length; y++)
- {
- for (var x = 0; x < ta[y].Length; x++)
- {
- var thisCell = ta[y][x];
- FixTopBorder(ta, thisCell, x, y);
- FixLeftBorder(ta, thisCell, x, y);
- FixBottomBorder(ta, thisCell, x, y);
- FixRightBorder(ta, thisCell, x, y);
- }
- }
- }
- private static void FixTopBorder(XElement[][] ta, XElement thisCell, int x, int y)
- {
- if (y > 0)
- {
- var rowAbove = ta[y - 1];
- if (x < rowAbove.Length - 1)
- {
- XElement cellAbove = ta[y - 1][x];
- if (cellAbove != null &&
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null &&
- cellAbove.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null)
- {
- ResolveCellBorder(
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.top).FirstOrDefault(),
- cellAbove.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.bottom).FirstOrDefault());
- }
- }
- }
- }
- private static void FixLeftBorder(XElement[][] ta, XElement thisCell, int x, int y)
- {
- if (x > 0)
- {
- XElement cellLeft = ta[y][x - 1];
- if (cellLeft != null &&
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null &&
- cellLeft.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null)
- {
- ResolveCellBorder(
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.left).FirstOrDefault(),
- cellLeft.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.right).FirstOrDefault());
- }
- }
- }
- private static void FixBottomBorder(XElement[][] ta, XElement thisCell, int x, int y)
- {
- if (y < ta.Length - 1)
- {
- var rowBelow = ta[y + 1];
- if (x < rowBelow.Length - 1)
- {
- XElement cellBelow = ta[y + 1][x];
- if (cellBelow != null &&
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null &&
- cellBelow.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null)
- {
- ResolveCellBorder(
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.bottom).FirstOrDefault(),
- cellBelow.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.top).FirstOrDefault());
- }
- }
- }
- }
- private static void FixRightBorder(XElement[][] ta, XElement thisCell, int x, int y)
- {
- if (x < ta[y].Length - 1)
- {
- XElement cellRight = ta[y][x + 1];
- if (cellRight != null &&
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null &&
- cellRight.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null)
- {
- ResolveCellBorder(
- thisCell.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.right).FirstOrDefault(),
- cellRight.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.left).FirstOrDefault());
- }
- }
- }
- private static readonly Dictionary<string, int> BorderTypePriority = new Dictionary<string, int>()
- {
- { "single", 1 },
- { "thick", 2 },
- { "double", 3 },
- { "dotted", 4 },
- };
- private static readonly Dictionary<string, int> BorderNumber = new Dictionary<string, int>()
- {
- {"single", 1 },
- {"thick", 2 },
- {"double", 3 },
- {"dotted", 4 },
- {"dashed", 5 },
- {"dotDash", 5 },
- {"dotDotDash", 5 },
- {"triple", 6 },
- {"thinThickSmallGap", 6 },
- {"thickThinSmallGap", 6 },
- {"thinThickThinSmallGap", 6 },
- {"thinThickMediumGap", 6 },
- {"thickThinMediumGap", 6 },
- {"thinThickThinMediumGap", 6 },
- {"thinThickLargeGap", 6 },
- {"thickThinLargeGap", 6 },
- {"thinThickThinLargeGap", 6 },
- {"wave", 7 },
- {"doubleWave", 7 },
- {"dashSmallGap", 5 },
- {"dashDotStroked", 5 },
- {"threeDEmboss", 7 },
- {"threeDEngrave", 7 },
- {"outset", 7 },
- {"inset", 7 },
- };
- private static void ResolveCellBorder(XElement border1, XElement border2)
- {
- if (border1 == null || border2 == null)
- return;
- if ((string)border1.Attribute(W.val) == "nil" || (string)border2.Attribute(W.val) == "nil")
- return;
- if ((string)border1.Attribute(W.sz) == "nil" || (string)border2.Attribute(W.sz) == "nil")
- return;
- var border1Val = (string)border1.Attribute(W.val);
- var border1Weight = 1;
- if (BorderNumber.ContainsKey(border1Val))
- border1Weight = BorderNumber[border1Val];
- var border2Val = (string)border2.Attribute(W.val);
- var border2Weight = 1;
- if (BorderNumber.ContainsKey(border2Val))
- border2Weight = BorderNumber[border2Val];
- if (border1Weight != border2Weight)
- {
- if (border1Weight < border2Weight)
- BorderOverride(border2, border1);
- else
- BorderOverride(border1, border2);
- }
- if ((decimal)border1.Attribute(W.sz) > (decimal)border2.Attribute(W.sz))
- {
- BorderOverride(border1, border2);
- return;
- }
- if ((decimal)border1.Attribute(W.sz) < (decimal)border2.Attribute(W.sz))
- {
- BorderOverride(border2, border1);
- return;
- }
- var border1Type = (string)border1.Attribute(W.val);
- var border2Type = (string)border2.Attribute(W.val);
- if (BorderTypePriority.ContainsKey(border1Type) &&
- BorderTypePriority.ContainsKey(border2Type))
- {
- var border1Pri = BorderTypePriority[border1Type];
- var border2Pri = BorderTypePriority[border2Type];
- if (border1Pri < border2Pri)
- {
- BorderOverride(border2, border1);
- return;
- }
- if (border2Pri < border1Pri)
- {
- BorderOverride(border1, border2);
- return;
- }
- }
- var color1Str = (string)border1.Attribute(W.color);
- if (color1Str == "auto")
- color1Str = "000000";
- var color2Str = (string)border2.Attribute(W.color);
- if (color2Str == "auto")
- color2Str = "000000";
- if (color1Str != null && color2Str != null && color1Str != color2Str)
- {
- try
- {
- var color1 = Convert.ToInt32(color1Str, 16);
- var color2 = Convert.ToInt32(color2Str, 16);
- if (color1 < color2)
- {
- BorderOverride(border1, border2);
- return;
- }
- if (color2 < color1)
- {
- BorderOverride(border2, border1);
- }
- }
- // if the above throws ArgumentException, FormatException, or OverflowException, then abort
- catch (Exception)
- {
- // Ignore
- }
- }
- }
- private static void BorderOverride(XElement fromBorder, XElement toBorder)
- {
- toBorder.Attribute(W.val).Value = fromBorder.Attribute(W.val).Value;
- if (fromBorder.Attribute(W.color) != null)
- toBorder.SetAttributeValue(W.color, fromBorder.Attribute(W.color).Value);
- if (fromBorder.Attribute(W.sz) != null)
- toBorder.SetAttributeValue(W.sz, fromBorder.Attribute(W.sz).Value);
- if (fromBorder.Attribute(W.themeColor) != null)
- toBorder.SetAttributeValue(W.themeColor, fromBorder.Attribute(W.themeColor).Value);
- if (fromBorder.Attribute(W.themeTint) != null)
- toBorder.SetAttributeValue(W.themeTint, fromBorder.Attribute(W.themeTint).Value);
- }
- private static void CalculateSpanWidthForTabs(WordprocessingDocument wordDoc)
- {
- // Note: when implementing a paging version of the HTML transform, this needs to be done
- // for all content parts, not just the main document part.
- // w:defaultTabStop in settings
- var sxd = wordDoc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
- var defaultTabStop = (int?)sxd.Descendants(W.defaultTabStop).Attributes(W.val).FirstOrDefault() ?? 720;
- var pxd = wordDoc.MainDocumentPart.GetXDocument();
- var root = pxd.Root;
- if (root == null) return;
- var newRoot = (XElement)CalculateSpanWidthTransform(root, defaultTabStop);
- root.ReplaceWith(newRoot);
- wordDoc.MainDocumentPart.PutXDocument();
- }
- // TODO: Refactor. This method is way too long.
- [SuppressMessage("ReSharper", "FunctionComplexityOverflow")]
- private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop)
- {
- var element = node as XElement;
- if (element == null) return node;
- // if it is not a paragraph or if there are no tabs in the paragraph,
- // then no need to continue processing.
- if (element.Name != W.p ||
- !element.DescendantsTrimmed(W.txbxContent).Where(d => d.Name == W.r).Elements(W.tab).Any())
- {
- // TODO: Revisit. Can we just return the node if it is a paragraph that does not have any tab?
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop)));
- }
- var clonedPara = new XElement(element);
- var leftInTwips = 0;
- var firstInTwips = 0;
- var ind = clonedPara.Elements(W.pPr).Elements(W.ind).FirstOrDefault();
- if (ind != null)
- {
- // todo need to handle start and end attributes
- var left = (int?)ind.Attribute(W.left);
- if (left != null)
- leftInTwips = (int)left;
- var firstLine = 0;
- var firstLineAtt = (int?)ind.Attribute(W.firstLine);
- if (firstLineAtt != null)
- firstLine = (int)firstLineAtt;
- var hangingAtt = (int?)ind.Attribute(W.hanging);
- if (hangingAtt != null)
- firstLine = -(int)hangingAtt;
- firstInTwips = leftInTwips + firstLine;
- }
- // calculate the tab stops, in twips
- var tabs = clonedPara
- .Elements(W.pPr)
- .Elements(W.tabs)
- .FirstOrDefault();
- if (tabs == null)
- {
- if (leftInTwips == 0)
- {
- tabs = new XElement(W.tabs,
- Enumerable.Range(1, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- }
- else
- {
- tabs = new XElement(W.tabs,
- new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, leftInTwips)));
- tabs = AddDefaultTabsAfterLastTab(tabs, defaultTabStop);
- }
- }
- else
- {
- if (leftInTwips != 0)
- {
- tabs.Add(
- new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, leftInTwips)));
- }
- tabs = AddDefaultTabsAfterLastTab(tabs, defaultTabStop);
- }
- var twipCounter = firstInTwips;
- var contentToMeasure = element.DescendantsTrimmed(z => z.Name == W.txbxContent || z.Name == W.pPr || z.Name == W.rPr).ToArray();
- var currentElementIdx = 0;
- while (true)
- {
- var currentElement = contentToMeasure[currentElementIdx];
- if (currentElement.Name == W.br)
- {
- twipCounter = leftInTwips;
- currentElement.Add(new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo,
- "{0:0.000}", (decimal)firstInTwips / 1440m)));
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- }
- if (currentElement.Name == W.tab)
- {
- var runContainingTabToReplace = currentElement.Parent;
- var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName") ??
- runContainingTabToReplace.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName");
- var testAmount = twipCounter;
- var tabAfterText = tabs
- .Elements(W.tab)
- .FirstOrDefault(t => (int)t.Attribute(W.pos) > testAmount);
- if (tabAfterText == null)
- {
- // something has gone wrong, so put 1/2 inch in
- if (currentElement.Attribute(PtOpenXml.TabWidth) == null)
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth, 720m));
- break;
- }
- var tabVal = (string)tabAfterText.Attribute(W.val);
- if (tabVal == "right" || tabVal == "end")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- var dummyRun2 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfTextAfterTab - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos), twipCounter + widthOfTextAfterTab);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- if (tabVal == "decimal")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- if (textAfterTab.Contains("."))
- {
- var mantissa = textAfterTab.Split('.')[0];
- var dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, mantissa));
- var widthOfMantissa = CalcWidthOfRunInTwips(dummyRun4);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfMantissa - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- var decims = textAfterTab.Substring(textAfterTab.IndexOf('.'));
- dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, decims));
- var widthOfDecims = CalcWidthOfRunInTwips(dummyRun4);
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos) + widthOfDecims, twipCounter + widthOfMantissa + widthOfDecims);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- else
- {
- var dummyRun2 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfTextAfterTab - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos), twipCounter + widthOfTextAfterTab);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- }
- if ((string)tabAfterText.Attribute(W.val) == "center")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- var dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfText = CalcWidthOfRunInTwips(dummyRun4);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - (widthOfText / 2) - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos) + widthOfText / 2, twipCounter + widthOfText);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- if (tabVal == "left" || tabVal == "start" || tabVal == "num")
- {
- var delta = (int)tabAfterText.Attribute(W.pos) - twipCounter;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = (int)tabAfterText.Attribute(W.pos);
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- }
- if (currentElement.Name == W.t)
- {
- // TODO: Revisit. This is a quick fix because it doesn't work on Azure.
- // Given the changes we've made elsewhere, though, this is not required
- // for the first tab at least. We could also enhance that other change
- // to deal with all tabs.
- //var runContainingTabToReplace = currentElement.Parent;
- //var paragraphForRun = runContainingTabToReplace.Ancestors(W.p).First();
- //var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.FontName) ??
- // paragraphForRun.Attribute(PtOpenXml.FontName);
- //var languageTypeAtt = runContainingTabToReplace.Attribute(PtOpenXml.LanguageType) ??
- // paragraphForRun.Attribute(PtOpenXml.LanguageType);
- //var dummyRun3 = new XElement(W.r, fontNameAtt, languageTypeAtt,
- // runContainingTabToReplace.Elements(W.rPr),
- // currentElement);
- //var widthOfText = CalcWidthOfRunInTwips(dummyRun3);
- const int widthOfText = 0;
- currentElement.Add(new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)widthOfText / 1440m)));
- twipCounter += widthOfText;
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop)));
- }
- private static XAttribute GetLeader(XElement tabAfterText)
- {
- var leader = (string)tabAfterText.Attribute(W.leader);
- if (leader == null)
- return null;
- return new XAttribute(PtOpenXml.Leader, leader);
- }
- private static XElement AddDefaultTabsAfterLastTab(XElement tabs, int defaultTabStop)
- {
- var lastTabElement = tabs
- .Elements(W.tab)
- .Where(t => (string)t.Attribute(W.val) != "clear" && (string)t.Attribute(W.val) != "bar")
- .OrderBy(t => (int)t.Attribute(W.pos))
- .LastOrDefault();
- if (lastTabElement != null)
- {
- if (defaultTabStop == 0)
- defaultTabStop = 720;
- var rangeStart = (int)lastTabElement.Attribute(W.pos) / defaultTabStop + 1;
- var tempTabs = new XElement(W.tabs,
- tabs.Elements().Where(t => (string)t.Attribute(W.val) != "clear" && (string)t.Attribute(W.val) != "bar"),
- Enumerable.Range(rangeStart, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- tempTabs = new XElement(W.tabs,
- tempTabs.Elements().OrderBy(t => (int)t.Attribute(W.pos)));
- return tempTabs;
- }
- else
- {
- tabs = new XElement(W.tabs,
- Enumerable.Range(1, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- }
- return tabs;
- }
- private static readonly HashSet<string> UnknownFonts = new HashSet<string>();
- private static HashSet<string> _knownFamilies;
- private static HashSet<string> KnownFamilies
- {
- get
- {
- if (_knownFamilies == null)
- {
- _knownFamilies = new HashSet<string>();
- var families = FontFamily.Families;
- foreach (var fam in families)
- _knownFamilies.Add(fam.Name);
- }
- return _knownFamilies;
- }
- }
- private static int CalcWidthOfRunInTwips(XElement r)
- {
- var fontName = (string)r.Attribute(PtOpenXml.pt + "FontName") ??
- (string)r.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName");
- if (fontName == null)
- throw new OpenXmlPowerToolsException("Internal Error, should have FontName attribute");
- if (UnknownFonts.Contains(fontName))
- return 0;
- var rPr = r.Element(W.rPr);
- if (rPr == null)
- throw new OpenXmlPowerToolsException("Internal Error, should have run properties");
- var sz = GetFontSize(r) ?? 22m;
- // unknown font families will throw ArgumentException, in which case just return 0
- if (!KnownFamilies.Contains(fontName))
- return 0;
- // in theory, all unknown fonts are found by the above test, but if not...
- FontFamily ff;
- try
- {
- ff = new FontFamily(fontName);
- }
- catch (ArgumentException)
- {
- UnknownFonts.Add(fontName);
- return 0;
- }
- var fs = FontStyle.Regular;
- if (GetBoolProp(rPr, W.b) || GetBoolProp(rPr, W.bCs))
- fs |= FontStyle.Bold;
- if (GetBoolProp(rPr, W.i) || GetBoolProp(rPr, W.iCs))
- fs |= FontStyle.Italic;
- // Appended blank as a quick fix to accommodate that will get
- // appended to some layout-critical runs such as list item numbers.
- // In some cases, this might not be required or even wrong, so this
- // must be revisited.
- // TODO: Revisit.
- var runText = r.DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate() + " ";
- var tabLength = r.DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.tab)
- .Select(t => (decimal)t.Attribute(PtOpenXml.TabWidth))
- .Sum();
- if (runText.Length == 0 && tabLength == 0)
- return 0;
- int multiplier = 1;
- if (runText.Length <= 2)
- multiplier = 100;
- else if (runText.Length <= 4)
- multiplier = 50;
- else if (runText.Length <= 8)
- multiplier = 25;
- else if (runText.Length <= 16)
- multiplier = 12;
- else if (runText.Length <= 32)
- multiplier = 6;
- if (multiplier != 1)
- {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < multiplier; i++)
- sb.Append(runText);
- runText = sb.ToString();
- }
- var w = MetricsGetter.GetTextWidth(ff, fs, sz, runText);
- return (int)(w / 96m * 1440m / multiplier + tabLength * 1440m);
- }
- private static void InsertAppropriateNonbreakingSpaces(WordprocessingDocument wordDoc)
- {
- foreach (var part in wordDoc.ContentParts())
- {
- var pxd = part.GetXDocument();
- var root = pxd.Root;
- if (root == null) return;
- var newRoot = (XElement)InsertAppropriateNonbreakingSpacesTransform(root);
- root.ReplaceWith(newRoot);
- part.PutXDocument();
- }
- }
- // Non-breaking spaces are not required if we use appropriate CSS, i.e., "white-space: pre-wrap;".
- // We only need to make sure that empty w:p elements are translated into non-empty h:p elements,
- // because empty h:p elements would be ignored by browsers.
- // Further, in addition to not being required, non-breaking spaces would change the layout behavior
- // of spans having consecutive spaces. Therefore, avoiding non-breaking spaces has the additional
- // benefit of leading to a more faithful representation of the Word document in HTML.
- private static object InsertAppropriateNonbreakingSpacesTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- // child content of run to look for
- // W.br
- // W.cr
- // W.dayLong
- // W.dayShort
- // W.drawing
- // W.monthLong
- // W.monthShort
- // W.noBreakHyphen
- // W.object
- // W.pgNum
- // W.pTab
- // W.separator
- // W.softHyphen
- // W.sym
- // W.t
- // W.tab
- // W.yearLong
- // W.yearShort
- if (element.Name == W.p)
- {
- // Translate empty paragraphs to paragraphs having one run with
- // a normal space. A non-breaking space, i.e., \x00A0, is not
- // required if we use appropriate CSS.
- bool hasContent = element
- .Elements()
- .Where(e => e.Name != W.pPr)
- .DescendantsAndSelf()
- .Any(e =>
- e.Name == W.dayLong ||
- e.Name == W.dayShort ||
- e.Name == W.drawing ||
- e.Name == W.monthLong ||
- e.Name == W.monthShort ||
- e.Name == W.noBreakHyphen ||
- e.Name == W._object ||
- e.Name == W.pgNum ||
- e.Name == W.ptab ||
- e.Name == W.separator ||
- e.Name == W.softHyphen ||
- e.Name == W.sym ||
- e.Name == W.t ||
- e.Name == W.tab ||
- e.Name == W.yearLong ||
- e.Name == W.yearShort
- );
- if (hasContent == false)
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => InsertAppropriateNonbreakingSpacesTransform(n)),
- new XElement(W.r,
- element.Elements(W.pPr).Elements(W.rPr),
- new XElement(W.t, " ")));
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => InsertAppropriateNonbreakingSpacesTransform(n)));
- }
- return node;
- }
- private class SectionAnnotation
- {
- public XElement SectionElement;
- }
- private static void AnnotateForSections(WordprocessingDocument wordDoc)
- {
- var xd = wordDoc.MainDocumentPart.GetXDocument();
- var document = xd.Root;
- if (document == null) return;
- var body = document.Element(W.body);
- if (body == null) return;
- // move last sectPr into last paragraph
- var lastSectPr = body.Elements(W.sectPr).LastOrDefault();
- if (lastSectPr != null)
- {
- // if the last thing in the document is a table, Word will always insert a paragraph following that.
- var lastPara = body
- .DescendantsTrimmed(W.txbxContent)
- .LastOrDefault(p => p.Name == W.p);
- if (lastPara != null)
- {
- var lastParaProps = lastPara.Element(W.pPr);
- if (lastParaProps != null)
- lastParaProps.Add(lastSectPr);
- else
- lastPara.Add(new XElement(W.pPr, lastSectPr));
- lastSectPr.Remove();
- }
- }
- var reverseDescendants = xd.Descendants().Reverse().ToList();
- var currentSection = InitializeSectionAnnotation(reverseDescendants);
- foreach (var d in reverseDescendants)
- {
- if (d.Name == W.sectPr)
- {
- if (d.Attribute(XNamespace.Xmlns + "w") == null)
- d.Add(new XAttribute(XNamespace.Xmlns + "w", W.w));
- currentSection = new SectionAnnotation()
- {
- SectionElement = d
- };
- }
- else
- d.AddAnnotation(currentSection);
- }
- }
- private static SectionAnnotation InitializeSectionAnnotation(IEnumerable<XElement> reverseDescendants)
- {
- var currentSection = new SectionAnnotation()
- {
- SectionElement = reverseDescendants.FirstOrDefault(e => e.Name == W.sectPr)
- };
- if (currentSection.SectionElement != null &&
- currentSection.SectionElement.Attribute(XNamespace.Xmlns + "w") == null)
- currentSection.SectionElement.Add(new XAttribute(XNamespace.Xmlns + "w", W.w));
- // todo what should the default section props be?
- if (currentSection.SectionElement == null)
- currentSection = new SectionAnnotation()
- {
- SectionElement = new XElement(W.sectPr,
- new XAttribute(XNamespace.Xmlns + "w", W.w),
- new XElement(W.pgSz,
- new XAttribute(W._w, 12240),
- new XAttribute(W.h, 15840)),
- new XElement(W.pgMar,
- new XAttribute(W.top, 1440),
- new XAttribute(W.right, 1440),
- new XAttribute(W.bottom, 1440),
- new XAttribute(W.left, 1440),
- new XAttribute(W.header, 720),
- new XAttribute(W.footer, 720),
- new XAttribute(W.gutter, 0)),
- new XElement(W.cols,
- new XAttribute(W.space, 720)),
- new XElement(W.docGrid,
- new XAttribute(W.linePitch, 360)))
- };
- return currentSection;
- }
- private static object CreateBorderDivs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, IEnumerable<XElement> elements)
- {
- return elements.GroupAdjacent(e =>
- {
- var pBdr = e.Elements(W.pPr).Elements(W.pBdr).FirstOrDefault();
- if (pBdr != null)
- {
- var indStr = string.Empty;
- var ind = e.Elements(W.pPr).Elements(W.ind).FirstOrDefault();
- if (ind != null)
- indStr = ind.ToString(SaveOptions.DisableFormatting);
- return pBdr.ToString(SaveOptions.DisableFormatting) + indStr;
- }
- return e.Name == W.tbl ? "table" : string.Empty;
- })
- .Select(g =>
- {
- if (g.Key == string.Empty)
- {
- return (object)GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m);
- }
- if (g.Key == "table")
- {
- return g.Select(gc => ConvertToHtmlTransform(wordDoc, settings, gc, false, 0));
- }
- var pPr = g.First().Elements(W.pPr).First();
- var pBdr = pPr.Element(W.pBdr);
- var style = new Dictionary<string, string>();
- GenerateBorderStyle(pBdr, W.top, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.right, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.bottom, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.left, style, BorderType.Paragraph);
- var currentMarginLeft = 0m;
- var ind = pPr.Element(W.ind);
- if (ind != null)
- {
- var leftInInches = (decimal?)ind.Attribute(W.left) / 1440m ?? 0;
- var hangingInInches = -(decimal?)ind.Attribute(W.hanging) / 1440m ?? 0;
- currentMarginLeft = leftInInches + hangingInInches;
- style.AddIfMissing("margin-left",
- currentMarginLeft > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", currentMarginLeft)
- : "0");
- }
- var div = new XElement(Xhtml.div,
- GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft));
- div.AddAnnotation(style);
- return div;
- })
- .ToList();
- }
- private static IEnumerable<object> GroupAndVerticallySpaceNumberedParagraphs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- IEnumerable<XElement> elements, decimal currentMarginLeft)
- {
- var grouped = elements
- .GroupAdjacent(e =>
- {
- var abstractNumId = (string)e.Attribute(PtOpenXml.pt + "AbstractNumId");
- if (abstractNumId != null)
- return "num:" + abstractNumId;
- var contextualSpacing = e.Elements(W.pPr).Elements(W.contextualSpacing).FirstOrDefault();
- if (contextualSpacing != null)
- {
- var styleName = (string)e.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
- if (styleName == null)
- return "";
- return "sty:" + styleName;
- }
- return "";
- })
- .ToList();
- var newContent = grouped
- .Select(g =>
- {
- if (g.Key == "")
- return g.Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
- var last = g.Count() - 1;
- return g.Select((e, i) => ConvertToHtmlTransform(wordDoc, settings, e, i != last, currentMarginLeft));
- });
- return (IEnumerable<object>)newContent;
- }
- private class BorderMappingInfo
- {
- public string CssName;
- public decimal CssSize;
- }
- private static readonly Dictionary<string, BorderMappingInfo> BorderStyleMap = new Dictionary<string, BorderMappingInfo>()
- {
- { "single", new BorderMappingInfo() { CssName = "solid", CssSize = 1.0m }},
- { "dotted", new BorderMappingInfo() { CssName = "dotted", CssSize = 1.0m }},
- { "dashSmallGap", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dashed", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dotDotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "double", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }},
- { "triple", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }},
- { "thinThickSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }},
- { "thickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }},
- { "thinThickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thinThickMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thinThickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }},
- { "thinThickLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "thickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "thinThickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }},
- { "wave", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }},
- { "doubleWave", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "dashDotStroked", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }},
- { "threeDEmboss", new BorderMappingInfo() { CssName = "ridge", CssSize = 6.0m }},
- { "threeDEngrave", new BorderMappingInfo() { CssName = "groove", CssSize = 6.0m }},
- { "outset", new BorderMappingInfo() { CssName = "outset", CssSize = 4.5m }},
- { "inset", new BorderMappingInfo() { CssName = "inset", CssSize = 4.5m }},
- };
- private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictionary<string, string> style, BorderType borderType)
- {
- string whichSide;
- if (sideXName == W.top)
- whichSide = "top";
- else if (sideXName == W.right)
- whichSide = "right";
- else if (sideXName == W.bottom)
- whichSide = "bottom";
- else
- whichSide = "left";
- if (pBdr == null)
- {
- style.Add("border-" + whichSide, "none");
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- style.Add("padding-" + whichSide, "5.4pt");
- return;
- }
- var side = pBdr.Element(sideXName);
- if (side == null)
- {
- style.Add("border-" + whichSide, "none");
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- style.Add("padding-" + whichSide, "5.4pt");
- return;
- }
- var type = (string)side.Attribute(W.val);
- if (type == "nil" || type == "none")
- {
- style.Add("border-" + whichSide + "-style", "none");
- var space = (decimal?)side.Attribute(W.space) ?? 0;
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- if (space < 5.4m)
- space = 5.4m;
- style.Add("padding-" + whichSide,
- space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space));
- }
- else
- {
- var sz = (int)side.Attribute(W.sz);
- var space = (decimal?)side.Attribute(W.space) ?? 0;
- var color = (string)side.Attribute(W.color);
- if (color == null || color == "auto")
- color = "windowtext";
- else
- color = ConvertColor(color);
- decimal borderWidthInPoints = Math.Max(1m, Math.Min(96m, Math.Max(2m, sz)) / 8m);
- var borderStyle = "solid";
- if (BorderStyleMap.ContainsKey(type))
- {
- var borderInfo = BorderStyleMap[type];
- borderStyle = borderInfo.CssName;
- if (type == "double")
- {
- if (sz <= 8)
- borderWidthInPoints = 2.5m;
- else if (sz <= 18)
- borderWidthInPoints = 6.75m;
- else
- borderWidthInPoints = sz / 3m;
- }
- else if (type == "triple")
- {
- if (sz <= 8)
- borderWidthInPoints = 8m;
- else if (sz <= 18)
- borderWidthInPoints = 11.25m;
- else
- borderWidthInPoints = 11.25m;
- }
- else if (type.ToLower().Contains("dash"))
- {
- if (sz <= 4)
- borderWidthInPoints = 1m;
- else if (sz <= 12)
- borderWidthInPoints = 1.5m;
- else
- borderWidthInPoints = 2m;
- }
- else if (type != "single")
- borderWidthInPoints = borderInfo.CssSize;
- }
- if (type == "outset" || type == "inset")
- color = "";
- var borderWidth = string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", borderWidthInPoints);
- style.Add("border-" + whichSide, borderStyle + " " + color + " " + borderWidth);
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- if (space < 5.4m)
- space = 5.4m;
- style.Add("padding-" + whichSide,
- space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space));
- }
- }
- private static readonly Dictionary<string, Func<string, string, string>> ShadeMapper = new Dictionary<string, Func<string, string, string>>()
- {
- { "auto", (c, f) => c },
- { "clear", (c, f) => f },
- { "nil", (c, f) => f },
- { "solid", (c, f) => c },
- { "diagCross", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "diagStripe", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "horzCross", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "horzStripe", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "pct10", (c, f) => ConvertColorFillPct(c, f, .1) },
- { "pct12", (c, f) => ConvertColorFillPct(c, f, .125) },
- { "pct15", (c, f) => ConvertColorFillPct(c, f, .15) },
- { "pct20", (c, f) => ConvertColorFillPct(c, f, .2) },
- { "pct25", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "pct30", (c, f) => ConvertColorFillPct(c, f, .3) },
- { "pct35", (c, f) => ConvertColorFillPct(c, f, .35) },
- { "pct37", (c, f) => ConvertColorFillPct(c, f, .375) },
- { "pct40", (c, f) => ConvertColorFillPct(c, f, .4) },
- { "pct45", (c, f) => ConvertColorFillPct(c, f, .45) },
- { "pct50", (c, f) => ConvertColorFillPct(c, f, .50) },
- { "pct55", (c, f) => ConvertColorFillPct(c, f, .55) },
- { "pct60", (c, f) => ConvertColorFillPct(c, f, .60) },
- { "pct62", (c, f) => ConvertColorFillPct(c, f, .625) },
- { "pct65", (c, f) => ConvertColorFillPct(c, f, .65) },
- { "pct70", (c, f) => ConvertColorFillPct(c, f, .7) },
- { "pct75", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "pct80", (c, f) => ConvertColorFillPct(c, f, .8) },
- { "pct85", (c, f) => ConvertColorFillPct(c, f, .85) },
- { "pct87", (c, f) => ConvertColorFillPct(c, f, .875) },
- { "pct90", (c, f) => ConvertColorFillPct(c, f, .9) },
- { "pct95", (c, f) => ConvertColorFillPct(c, f, .95) },
- { "reverseDiagStripe", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "thinDiagCross", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "thinDiagStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinHorzCross", (c, f) => ConvertColorFillPct(c, f, .3) },
- { "thinHorzStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinReverseDiagStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinVertStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- };
- private static readonly Dictionary<string, string> ShadeCache = new Dictionary<string, string>();
- // fill is the background, color is the foreground
- private static string ConvertColorFillPct(string color, string fill, double pct)
- {
- if (color == "auto")
- color = "000000";
- if (fill == "auto")
- fill = "ffffff";
- var key = color + fill + pct.ToString(CultureInfo.InvariantCulture);
- if (ShadeCache.ContainsKey(key))
- return ShadeCache[key];
- var fillRed = Convert.ToInt32(fill.Substring(0, 2), 16);
- var fillGreen = Convert.ToInt32(fill.Substring(2, 2), 16);
- var fillBlue = Convert.ToInt32(fill.Substring(4, 2), 16);
- var colorRed = Convert.ToInt32(color.Substring(0, 2), 16);
- var colorGreen = Convert.ToInt32(color.Substring(2, 2), 16);
- var colorBlue = Convert.ToInt32(color.Substring(4, 2), 16);
- var finalRed = (int)(fillRed - (fillRed - colorRed) * pct);
- var finalGreen = (int)(fillGreen - (fillGreen - colorGreen) * pct);
- var finalBlue = (int)(fillBlue - (fillBlue - colorBlue) * pct);
- var returnValue = string.Format("{0:x2}{1:x2}{2:x2}", finalRed, finalGreen, finalBlue);
- ShadeCache.Add(key, returnValue);
- return returnValue;
- }
- private static void CreateStyleFromShd(Dictionary<string, string> style, XElement shd)
- {
- if (shd == null)
- return;
- var shadeType = (string)shd.Attribute(W.val);
- var color = (string)shd.Attribute(W.color);
- var fill = (string)shd.Attribute(W.fill);
- if (ShadeMapper.ContainsKey(shadeType))
- {
- color = ShadeMapper[shadeType](color, fill);
- }
- if (color != null)
- {
- var cvtColor = ConvertColor(color);
- if (!string.IsNullOrEmpty(cvtColor))
- style.AddIfMissing("background", cvtColor);
- }
- }
- private static readonly Dictionary<string, string> NamedColors = new Dictionary<string, string>()
- {
- {"black", "black"},
- {"blue", "blue" },
- {"cyan", "aqua" },
- {"green", "green" },
- {"magenta", "fuchsia" },
- {"red", "red" },
- {"yellow", "yellow" },
- {"white", "white" },
- {"darkBlue", "#00008B" },
- {"darkCyan", "#008B8B" },
- {"darkGreen", "#006400" },
- {"darkMagenta", "#800080" },
- {"darkRed", "#8B0000" },
- {"darkYellow", "#808000" },
- {"darkGray", "#A9A9A9" },
- {"lightGray", "#D3D3D3" },
- {"none", "" },
- };
- private static void CreateColorProperty(string propertyName, string color, Dictionary<string, string> style)
- {
- if (color == null)
- return;
- // "auto" color is black for "color" and white for "background" property.
- if (color == "auto")
- color = propertyName == "color" ? "black" : "white";
- if (NamedColors.ContainsKey(color))
- {
- var lc = NamedColors[color];
- if (lc == "")
- return;
- style.AddIfMissing(propertyName, lc);
- return;
- }
- style.AddIfMissing(propertyName, "#" + color);
- }
- private static string ConvertColor(string color)
- {
- // "auto" color is black for "color" and white for "background" property.
- // As this method is only called for "background" colors, "auto" is translated
- // to "white" and never "black".
- if (color == "auto")
- color = "white";
- if (NamedColors.ContainsKey(color))
- {
- var lc = NamedColors[color];
- if (lc == "")
- return "black";
- return lc;
- }
- return "#" + color;
- }
- private static readonly Dictionary<string, string> FontFallback = new Dictionary<string, string>()
- {
- { "Arial", @"'{0}', 'sans-serif'" },
- { "Arial Narrow", @"'{0}', 'sans-serif'" },
- { "Arial Rounded MT Bold", @"'{0}', 'sans-serif'" },
- { "Arial Unicode MS", @"'{0}', 'sans-serif'" },
- { "Baskerville Old Face", @"'{0}', 'serif'" },
- { "Berlin Sans FB", @"'{0}', 'sans-serif'" },
- { "Berlin Sans FB Demi", @"'{0}', 'sans-serif'" },
- { "Calibri Light", @"'{0}', 'sans-serif'" },
- { "Gill Sans MT", @"'{0}', 'sans-serif'" },
- { "Gill Sans MT Condensed", @"'{0}', 'sans-serif'" },
- { "Lucida Sans", @"'{0}', 'sans-serif'" },
- { "Lucida Sans Unicode", @"'{0}', 'sans-serif'" },
- { "Segoe UI", @"'{0}', 'sans-serif'" },
- { "Segoe UI Light", @"'{0}', 'sans-serif'" },
- { "Segoe UI Semibold", @"'{0}', 'sans-serif'" },
- { "Tahoma", @"'{0}', 'sans-serif'" },
- { "Trebuchet MS", @"'{0}', 'sans-serif'" },
- { "Verdana", @"'{0}', 'sans-serif'" },
- { "Book Antiqua", @"'{0}', 'serif'" },
- { "Bookman Old Style", @"'{0}', 'serif'" },
- { "Californian FB", @"'{0}', 'serif'" },
- { "Cambria", @"'{0}', 'serif'" },
- { "Constantia", @"'{0}', 'serif'" },
- { "Garamond", @"'{0}', 'serif'" },
- { "Lucida Bright", @"'{0}', 'serif'" },
- { "Lucida Fax", @"'{0}', 'serif'" },
- { "Palatino Linotype", @"'{0}', 'serif'" },
- { "Times New Roman", @"'{0}', 'serif'" },
- { "Wide Latin", @"'{0}', 'serif'" },
- { "Courier New", @"'{0}'" },
- { "Lucida Console", @"'{0}'" },
- };
- private static void CreateFontCssProperty(string font, Dictionary<string, string> style)
- {
- if (FontFallback.ContainsKey(font))
- {
- style.AddIfMissing("font-family", string.Format(FontFallback[font], font));
- return;
- }
- style.AddIfMissing("font-family", font);
- }
- private static bool GetBoolProp(XElement runProps, XName xName)
- {
- var p = runProps.Element(xName);
- if (p == null)
- return false;
- var v = p.Attribute(W.val);
- if (v == null)
- return true;
- var s = v.Value.ToLower();
- if (s == "0" || s == "false")
- return false;
- if (s == "1" || s == "true")
- return true;
- return false;
- }
- private static object ConvertContentThatCanContainFields(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- IEnumerable<XElement> elements)
- {
- var grouped = elements
- .GroupAdjacent(e =>
- {
- var stack = e.Annotation<Stack<FieldRetriever.FieldElementTypeInfo>>();
- return stack == null || !stack.Any() ? (int?)null : stack.Select(st => st.Id).Min();
- })
- .ToList();
- var txformed = grouped
- .Select(g =>
- {
- var key = g.Key;
- if (key == null)
- return (object)g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m));
- var instrText = FieldRetriever.InstrText(g.First().Ancestors().Last(), (int)key)
- .TrimStart('{').TrimEnd('}');
- var parsed = FieldRetriever.ParseField(instrText);
- if (parsed.FieldType != "HYPERLINK")
- return g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m));
- var content = g.DescendantsAndSelf(W.r).Select(run => ConvertRun(wordDoc, settings, run));
- var a = parsed.Arguments.Length > 0
- ? new XElement(Xhtml.a, new XAttribute("href", parsed.Arguments[0]), content)
- : new XElement(Xhtml.a, content);
- var a2 = a as XElement;
- if (!a2.Nodes().Any())
- {
- a2.Add(new XText(""));
- return a2;
- }
- return a;
- })
- .ToList();
- return txformed;
- }
- #region Image Processing
- // Don't process wmf files (with contentType == "image/x-wmf") because GDI consumes huge amounts
- // of memory when dealing with wmf perhaps because it loads a DLL to do the rendering?
- // It actually works, but is not recommended.
- public static readonly List<string> ImageContentTypes = new List<string>
- {
- "image/png", "image/gif", "image/tiff", "image/jpeg","image/x-wmf","image/x-emf","application/vnd.openxmlformats-officedocument.oleObject"
- };
- public static XElement ProcessImage(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- if (imageHandler == null)
- {
- return null;
- }
- if (element.Name == W.drawing)
- {
- return ProcessDrawing(wordDoc, element, imageHandler);
- }
- if (element.Name == W.pict || element.Name == W._object)
- {
- return ProcessPictureOrObject(wordDoc, element , imageHandler);
- }
-
- return null;
- }
- private static XElement ProcessDrawing(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- var containerElement = element.Elements()
- .FirstOrDefault(e => e.Name == WP.inline || e.Name == WP.anchor);
- if (containerElement == null) return null;
- string hyperlinkUri = null;
- var hyperlinkElement = element
- .Elements(WP.inline)
- .Elements(WP.docPr)
- .Elements(A.hlinkClick)
- .FirstOrDefault();
- if (hyperlinkElement != null)
- {
- var rId = (string)hyperlinkElement.Attribute(R.id);
- if (rId != null)
- {
- var hyperlinkRel = wordDoc.MainDocumentPart.HyperlinkRelationships.FirstOrDefault(hlr => hlr.Id == rId);
- if (hyperlinkRel != null)
- {
- hyperlinkUri = hyperlinkRel.Uri.ToString();
- }
- }
- }
- var extentCx = (int?)containerElement.Elements(WP.extent)
- .Attributes(NoNamespace.cx).FirstOrDefault();
- var extentCy = (int?)containerElement.Elements(WP.extent)
- .Attributes(NoNamespace.cy).FirstOrDefault();
- var altText = (string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.descr).FirstOrDefault() ??
- ((string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.name).FirstOrDefault() ?? "");
- if (containerElement.Elements(A.graphic)
- .Elements(A.graphicData) != null)
- {
- XElement rest = null;
- var elms = containerElement.Elements(A.graphic)
- .Elements(A.graphicData).Elements();
- foreach (var elm in elms)
- {
- if (elm.Name == Pic._pic)
- {
- rest= ProcessPic(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- if (elm.Name == C.chart)
- {
- rest = ProcessChart(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- if (elm.Name == DGM.relIds)
- {
- rest = ProcessDgm(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- }
- return rest;
- }
- else {
- return null;
- }
- }
- public static XElement ProcessPic(WordprocessingDocument wordDoc, XElement containerElement, Func<ImageInfo, XElement> imageHandler,
- int? extentCx,int? extentCy,string altText, XElement element,string hyperlinkUri) {
- var blipFill = containerElement.Elements(A.graphic)
- .Elements(A.graphicData)
- .Elements(Pic._pic).Elements(Pic.blipFill).FirstOrDefault();
- if (blipFill == null) return null;
- var imageRid = (string)blipFill.Elements(A.blip).Attributes(R.embed).FirstOrDefault();
- if (imageRid == null) return null;
- var pp3 = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp => pp.RelationshipId == imageRid);
- if (pp3 == null) return null;
- var imagePart = (ImagePart)pp3.OpenXmlPart;
- if (imagePart == null) return null;
- // If the image markup points to a NULL image, then following will throw an ArgumentOutOfRangeException
- try
- {
- imagePart = (ImagePart)wordDoc.MainDocumentPart.GetPartById(imageRid);
- }
- catch (ArgumentOutOfRangeException)
- {
- return null;
- }
- var contentType = imagePart.ContentType;
- if (!ImageContentTypes.Contains(contentType))
- return null;
- var uil= imagePart.Uri;
- //WORDHelper.GetWordNodeByPath(wordDoc,"");
- var Stream = imagePart.GetStream();
- try
- {
- if (contentType == "image/x-wmf" || contentType == "image/x-emf")
- {
- return ProcessWmf(Stream, contentType, element, hyperlinkUri, imageHandler);
- }
- else
- {
- using (var bitmap = new Bitmap(Stream))
- {
- if (extentCx != null && extentCy != null)
- {
- var imageInfo = new ImageInfo()
- {
- Bitmap = bitmap,
- ImgStyleAttribute = new XAttribute("style",
- string.Format(NumberFormatInfo.InvariantInfo,
- "width: {0}px; ",
- // "height: {1}in",
- (float)extentCx * 96.0 / (float)ImageInfo.EmusPerInch
- // ,
- //(float)extentCy*96.0 / (float)ImageInfo.EmusPerInch
- )),
- ContentType = contentType,
- DrawingElement = element,
- AltText = altText,
- };
- var imgElement2 = imageHandler(imageInfo);
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- imgElement2);
- }
- return imgElement2;
- }
- var imageInfo2 = new ImageInfo()
- {
- Bitmap = bitmap,
- ContentType = contentType,
- DrawingElement = element,
- AltText = altText,
- };
- var imgElement = imageHandler(imageInfo2);
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- imgElement);
- }
- return imgElement;
- }
- }
- }
- catch
- {
- XElement img = null;
- byte[] bytes = new byte[Stream.Length];
- Stream.Read(bytes, 0, bytes.Length);
- // 设置当前流的位置为流的开始
- Stream.Seek(0, SeekOrigin.Begin);
- string url = System.Convert.ToBase64String(bytes);
- img = new XElement(Xhtml.img,
- new XAttribute(NoNamespace.src, "data:" + contentType + ";base64," + url),
- new XAttribute(NoNamespace.alt, "wrong image"));
- return img;
- }
- finally {
- Stream.Close();
- }
- }
- public static XElement ProcessWmf(Stream stream,string contentType, XElement element, string hyperlinkUri ,Func<ImageInfo, XElement> imageHandler)
- {
- XElement img = null;
- string mathxml = "";
- if (contentType.Equals("image/x-wmf"))
- {
- bool compatible = false;
- bool replaceSymbolFont = false;
- WMFConverter.Wmf.WmfParser parser = new WMFConverter.Wmf.WmfParser();
- WMFConverter.Svg.SvgGdi gdi = new WMFConverter.Svg.SvgGdi(compatible);
- gdi.ReplaceSymbolFont = replaceSymbolFont;
- parser.Parse(stream, gdi);
- mathxml = gdi.Document.InnerXml;
- }
- else if (contentType.Equals("image/x-emf"))
- {
- MemoryStream memoryStream = new MemoryStream();
- //Metafile inFile = new Metafile(stream);
- ////Metafile outFile = new Metafile(memoryStream, Graphics.FromHwnd(IntPtr.Zero).GetHdc(), EmfType.EmfOnly);
- //inFile.Save(memoryStream, ImageFormat.Png);
- //var ba = memoryStream.ToArray();
- //mathxml = System.Convert.ToBase64String(ba);
- //System.Drawing.Bitmap inFile = new System.Drawing.Bitmap(stream);
- //Graphics graphics = Graphics.FromImage(inFile);
- //graphics.Save();
- //graphics.Dispose();
- //inFile.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
- //byte[] arr = new byte[memoryStream.Length];
- //memoryStream.Position = 0;
- //memoryStream.Read(arr, 0, (int)memoryStream.Length);
- //memoryStream.Close();
- //memoryStream.Dispose();
- //mathxml = Convert.ToBase64String(arr);
- mathxml = "EmfBase64ConvertError";
- }
- try
- {
- var imageInfo = new ImageInfo()
- {
- ContentType = contentType,
- DrawingElement = element,
- AltText = "",
- Mathxml = mathxml
- };
- var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault();
- img =imageHandler(imageInfo);
- var tokens = style.Split(';');
- var widthInPoints = WidthInPoints(tokens);
- var heightInPoints = HeightInPoints(tokens);
- //var imgElement2 = imageHandler(imageInfo);
- if (widthInPoints != null && heightInPoints != null)
- {
- //imageInfo.ImgStyleAttribute = new XAttribute("style",
- // string.Format(NumberFormatInfo.InvariantInfo,
- // "width: {0}pt; height: {1}pt ; vertical-align: bottom;", widthInPoints, heightInPoints));
- img.SetAttributeValue("style", string.Format(NumberFormatInfo.InvariantInfo,
- "width: {0}pt; height: {1}pt ; vertical-align: bottom;", widthInPoints, heightInPoints));
- }
- else
- {
- //imageInfo.ImgStyleAttribute = new XAttribute("style",
- // string.Format(NumberFormatInfo.InvariantInfo,
- // "vertical-align: middle;"));
- img.SetAttributeValue("style", string.Format(NumberFormatInfo.InvariantInfo,
- "vertical-align: middle;"));
- }
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- img);
- }
- return img;
- }
- catch (OutOfMemoryException)
- {
- // the Bitmap class can throw OutOfMemoryException, which means the bitmap is messed up, so punt.
- return null;
- }
- catch (ArgumentException e)
- {
- throw new Exception(e.StackTrace);
- }
-
- }
- private static XElement ProcessPictureOrObject(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- var imageRid = (string)element.Elements(VML.shape).Elements(VML.imagedata).Attributes(R.id).FirstOrDefault();
- if (imageRid == null) return null;
- var pp = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp2 => pp2.RelationshipId == imageRid);
- Stream stream = null ;
- try
- {
- if (pp == null) return null;
- if (!pp.OpenXmlPart.ContentType.Equals("image/x-wmf") && !pp.OpenXmlPart.ContentType.Equals("image/x-emf"))
- {
- return ProcessObject(wordDoc, element, imageHandler);
- }
- string mathxml = "";
- if (pp.OpenXmlPart.ContentType.Equals("image/x-wmf"))
- {
- stream= pp.OpenXmlPart.GetStream();
- bool compatible = false;
- bool replaceSymbolFont = false;
- WMFConverter.Wmf.WmfParser parser = new WMFConverter.Wmf.WmfParser();
- WMFConverter.Svg.SvgGdi gdi = new WMFConverter.Svg.SvgGdi(compatible);
- gdi.ReplaceSymbolFont = replaceSymbolFont;
- parser.Parse(stream, gdi);
- mathxml = gdi.Document.InnerXml;
- }
- else if (pp.OpenXmlPart.ContentType.Equals("image/x-emf"))
- {
- ////var image= SixLabors.ImageSharp.Image.Load(pp.OpenXmlPart.GetStream());
- ////image.Save(memoryStream, new SixLabors.ImageSharp.Formats.Png.PngEncoder());
- //var stm= pp.OpenXmlPart.GetStream();
- //var parser = new EMF2StringParser();
- //parser.LineBreakCandidates = new[] { EmfPlusRecordType.EmfSelectObject, EmfPlusRecordType.EmfDeleteObject };
- //parser.SpaceCandidates = new[] { EmfPlusRecordType.EmfIntersectClipRect };
- //Metafile inFile = new Metafile(stm);
- //parser.LoadMetaFile(inFile);
- //parser.IsParseFailedLoggingEnabled = true;
- //var expected = parser.GetCombinedStringFromLoadedMetaFile();
- ////MemoryStream memoryStream = new MemoryStream();
- ////byte[] bytes = new byte[stm.Length];
- ////stm.Read(bytes, 0, bytes.Length);
- ////stm.Seek(0, SeekOrigin.Begin);
- ////
- ////Metafile outFile = new Metafile(memoryStream, Graphics.FromHwnd(IntPtr.Zero).GetHdc(), EmfType.EmfOnly);
- //// inFile.Save(memoryStream, ImageFormat.Png);
- ////var ba = memoryStream.ToArray();
- //application.EnablePartialTrustCode = true;
- //mathxml = expected;
- // MemoryStream memoryStream = new MemoryStream();
- //Metafile inFile = new Metafile(pp.OpenXmlPart.GetStream());
- ////Metafile outFile = new Metafile(memoryStream, Graphics.FromHwnd(IntPtr.Zero).GetHdc(), EmfType.EmfOnly);
- //inFile.Save(memoryStream, ImageFormat.Png);
- //var ba = memoryStream.ToArray();
- //mathxml = System.Convert.ToBase64String(ba);
- //System.Drawing.Bitmap inFile = new System.Drawing.Bitmap(pp.OpenXmlPart.GetStream());
- //Graphics graphics = Graphics.FromImage(inFile);
- //graphics.Save();
- //graphics.Dispose();
- //inFile.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
- //byte[] arr = new byte[memoryStream.Length];
- //memoryStream.Position = 0;
- //memoryStream.Read(arr, 0, (int)memoryStream.Length);
- //memoryStream.Close();
- //memoryStream.Dispose();
- // mathxml = Convert.ToBase64String(arr);
- mathxml = "EmfBase64ConvertError";
- }
- string hyperlinkUri = null;
- var contentType = pp.OpenXmlPart.ContentType;
- if (!ImageContentTypes.Contains(contentType))
- return null;
- try
- {
- XElement img = null;
- var imageInfo = new ImageInfo()
- {
- ContentType = contentType,
- DrawingElement = element,
- AltText = "",
- Mathxml = mathxml,
- };
- var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault();
- // if (style == null) {
- // return imageHandler(imageInfo);
- img = imageHandler(imageInfo);
- // }
- var tokens = style.Split(';');
- var widthInPoints = WidthInPoints(tokens);
- var heightInPoints = HeightInPoints(tokens);
- // var imgElement2 = imageHandler(imageInfo);
- if (widthInPoints != null && heightInPoints != null)
- {
- //imageInfo.ImgStyleAttribute = new XAttribute("style",
- // string.Format(NumberFormatInfo.InvariantInfo,
- // "width: {0}pt; height: {1}pt ; vertical-align: bottom;", widthInPoints, heightInPoints));
- img.SetAttributeValue("style", string.Format(NumberFormatInfo.InvariantInfo,
- "width: {0}pt; height: {1}pt ; vertical-align: bottom;", widthInPoints, heightInPoints));
- }
- else
- {
- //imageInfo.ImgStyleAttribute = new XAttribute("style",
- // string.Format(NumberFormatInfo.InvariantInfo,
- // "vertical-align: middle;"));
- img.SetAttributeValue("style", string.Format(NumberFormatInfo.InvariantInfo,
- "vertical-align: middle;"));
- }
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- img);
- }
- return img;
- }
- catch (OutOfMemoryException)
- {
- // the Bitmap class can throw OutOfMemoryException, which means the bitmap is messed up, so punt.
- return null;
- }
- catch (ArgumentException e)
- {
- throw new Exception(e.StackTrace);
- }
- }
- catch (ArgumentOutOfRangeException)
- {
- return null;
- }
- finally {
- if(stream!=null)
- stream.Close();
- }
- }
-
- private static XElement ProcessObject(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- var imageRid = (string)element.Elements(VML.shape).Elements(VML.imagedata).Attributes(R.id).FirstOrDefault();
- if (imageRid == null) return null;
- try
- {
- var pp = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp2 => pp2.RelationshipId == imageRid);
- if (pp == null) return null;
- var imagePart = (ImagePart)pp.OpenXmlPart;
- if (imagePart == null) return null;
- string hyperlinkUri = null;
-
- // WORDHelper.GetWordNodeByPath(wordDoc.MainDocumentPart.GetXDocument(), "//[@name='"+imagePart.Uri+"']");
- using (var partStream = imagePart.GetStream())
- {
- var contentType = imagePart.ContentType;
- if (!ImageContentTypes.Contains(contentType))
- return null;
- try
- {
- using (var bitmap = new Bitmap(partStream))
- {
- var imageInfo = new ImageInfo()
- {
- Bitmap = bitmap,
- ContentType = contentType,
- DrawingElement = element,
- AltText = ""
- };
- var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault();
- if (style == null) return imageHandler(imageInfo);
- var tokens = style.Split(';');
- var widthInPoints = WidthInPoints(tokens);
- var heightInPoints = HeightInPoints(tokens);
- if (widthInPoints != null && heightInPoints != null)
- {
- imageInfo.ImgStyleAttribute = new XAttribute("style",
- string.Format(NumberFormatInfo.InvariantInfo,
- "width: {0}pt; height: {1}pt", widthInPoints, heightInPoints));
- }
- var imgElement2 = imageHandler(imageInfo);
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- imgElement2);
- }
- return imgElement2;
- }
- }
- catch (OutOfMemoryException)
- {
- return null;
- }
- catch (ArgumentException e)
- {
- return null; //throw new Exception(e.StackTrace);
- }
- finally { partStream.Close(); }
- }
- }
- catch (ArgumentOutOfRangeException)
- {
- return null;
- }
-
- }
- private static float? HeightInPoints(IEnumerable<string> tokens)
- {
- return SizeInPoints(tokens, "height");
- }
- private static float? WidthInPoints(IEnumerable<string> tokens)
- {
- return SizeInPoints(tokens, "width");
- }
- private static float? SizeInPoints(IEnumerable<string> tokens, string name)
- {
- var sizeString = tokens
- .Select(t => new
- {
- Name = t.Split(':').First(),
- Value = t.Split(':').Skip(1).Take(1).FirstOrDefault()
- })
- .Where(p => p.Name == name)
- .Select(p => p.Value)
- .FirstOrDefault();
- if (sizeString != null &&
- sizeString.Length > 2 &&
- sizeString.Substring(sizeString.Length - 2) == "pt")
- {
- float size;
- if (float.TryParse(sizeString.Substring(0, sizeString.Length - 2), out size))
- return size;
- }
- return null;
- }
- #endregion
- }
- public static class HtmlConverterExtensions
- {
- public static void AddIfMissing(this Dictionary<string, string> style, string propName, string value)
- {
- if (style.ContainsKey(propName))
- return;
- style.Add(propName, value);
- }
- }
- }
|