12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430 |
- // 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;
- using HTEXLib.COMM.Helpers;
- using HTEXLib.DOCX.OpenXmlTool;
- using HTEXLib.Helpers;
- using HTEXLib.Helpers.ShapeHelpers;
- using HTEXLib.Models.Inner;
- using HTEXLib.PPTX.Models;
- // 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
- ),
- new XAttribute("target", "_blank"
- ),
- 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)
- {
- try
- {
- return ProcessTableRow(wordDoc, settings, element, currentMarginLeft);
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- // Transform w:tc to h:td.
- if (element.Name == W.tc)
- {
- try
- {
- return ProcessTableCell(wordDoc, settings, element);
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- // Transform images
- if (element.Name == W.drawing || element.Name == W.pict || element.Name == W._object)
- {
- try
- {
- XElement xElement = ProcessImage(wordDoc, element, settings.ImageHandler);
- return xElement;
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- // Transform content controls.
- if (element.Name == W.sdt)
- {
- try
- {
- return ProcessContentControl(wordDoc, settings, element, currentMarginLeft);
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- // Transform smart tags and simple fields.
- if (element.Name == W.smartTag || element.Name == W.fldSimple)
- {
- try
- {
- return CreateBorderDivs(wordDoc, settings, element.Elements());
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- //处理 数学公式
- if (element.Name == M.oMath)
- {
- try
- {
- return ProcessOMath(element);
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- if (element.Name == M.oMathPara)
- {
- try
- {
- return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- //处理形状
- if (element.Name == MC.AlternateContent)
- {
- try
- {
- return ProcessAlternateContent(wordDoc, element, settings.ImageHandler);
- }
- catch (Exception ex)
- {
- throw new TransException(element, $"{element.Name}{ex.Message}", ex);
- }
- }
- //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)
- {
- /*
- <mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
- <mc:Choice Requires="w16se">
- <w16se:symEx w16se:font="Segoe UI Emoji" w16se:char="2192" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" />
- </mc:Choice>
- <mc:Fallback>
- <w:t xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">→</w:t>
- </mc:Fallback>
- </mc:AlternateContent>
- */
- var ch= element.Element(MC.Choice).Element(W.drawing);
- if (ch!=null)
- {
- 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)
- {
- (string html, HTEXLib.Shape shape) = ProcessWps(wordDoc, elm, imageHandler);
- XElement img;
- string mathxml = Globals.UnsupportedImage;
- var imageInfo = new ImageInfo()
- {
- ContentType = "image/x-emf",
- DrawingElement = element,
- AltText = "UnsupportedImageError",
- Mathxml = mathxml
- };
- double? widthInPoints = shape.style.position.cx;
- double? heightInPoints = shape.style.position.cy;
- img = 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;"));
- }
- // XElement elmt = XElement.Parse(html);
- return img;
- }
- else if (elm.Name == WPG.wgp)
- {
- return ProcessWgp(wordDoc, elm, imageHandler);
- }
- }
- }
- else {
- var wt= element.Element(MC.Fallback).Element(W.t);
- if (wt!=null) {
- return XElement.Parse($"<span>{wt.Value}</span>");
- }
- }
- return null;
- }
- public static XElement ProcessWgp(WordprocessingDocument wordDoc, XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- var grpSpPr = element.Element(WPG.grpSpPr);
- var ext = grpSpPr.GetWordNodeByPath("a:xfrm/a:ext");
- var cx = ext.Attribute("cx").Value;
- var cy = ext.Attribute("cy").Value;
- //var elms= element.Descendants(WPS.wsp);
- //StringBuilder stringBuilder = new StringBuilder();
- //foreach (var elm in elms) {
- // var emlt= ProcessWps(wordDoc, elm, imageHandler);
- // stringBuilder.Append(emlt);
- //}
- //string svg = stringBuilder.ToString();
- //svg = $"<div>{svg}</div>";
- //XElement elmt = XElement.Parse(svg);
- XElement img;
- string mathxml = Globals.UnsupportedImage;
- var imageInfo = new ImageInfo()
- {
- ContentType = "image/x-emf",
- DrawingElement = element,
- AltText = "UnsupportedImageError",
- Mathxml = mathxml
- };
- double? widthInPoints = int.Parse(cx) * 96.0 / 914400;
- double? heightInPoints = int.Parse(cy) * 96.0 / 914400;
- img = 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;"));
- }
- return img;
- }
- 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 };
- XElement img = null;
- string mathxml = Globals.UnsupportedImage;
- var imageInfo = new ImageInfo()
- {
- ContentType = "image/x-emf",
- DrawingElement = element,
- AltText = "UnsupportedImageError",
- Mathxml = mathxml
- };
- img = imageHandler(imageInfo);
- double? widthInPoints = extentCx * 96.0 / 914400;
- double? heightInPoints = extentCy * 96.0 / 914400;
- 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;"));
- }
- return img;
- }
- 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);
- XElement img = null;
- string mathxml = Globals.UnsupportedImage;
- var imageInfo = new ImageInfo()
- {
- ContentType = "image/x-emf",
- DrawingElement = element,
- AltText = "UnsupportedImageError",
- Mathxml = mathxml
- };
- img = imageHandler(imageInfo);
- double? widthInPoints = extentCx * 96.0 / 914400;
- double? heightInPoints = extentCy * 96.0 / 914400;
- 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;"));
- }
- return img;
- }
- public static (string html, HTEXLib.Shape shape) 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, pars), shape);
- }
- public static string DrawingShape(HTEXLib.Shape shape, List<string> pars)
- {
- 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} />");
- if (pars.IsNotEmpty())
- {
- builder.Append($"<text x='{shape.style.position.cx / 2}' y='{shape.style.position.cy}' fill='#000000'>{string.Join("", pars)}</text>");
- }
- 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));
- if (rPr.Element(W.em) != null)
- {
- content = new XElement(Xhtml.dot, content);
- }
- // 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 defaultTabStopValue = (string)sxd.Descendants(W.defaultTabStop).Attributes(W.val).FirstOrDefault();
- var defaultTabStop = defaultTabStopValue != null ? WordprocessingMLUtil.StringToTwips(defaultTabStopValue) : 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 = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.left));
- if (left != null)
- leftInTwips = (int)left;
- var firstLine = 0;
- var firstLineAtt = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.firstLine));
- if (firstLineAtt != null)
- firstLine = (int)firstLineAtt;
- var hangingAtt = WordprocessingMLUtil.AttributeToTwips(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 => WordprocessingMLUtil.StringToTwips((string)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 = WordprocessingMLUtil.StringToTwips((string)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(WordprocessingMLUtil.StringToTwips((string)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 = WordprocessingMLUtil.StringToTwips((string)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(WordprocessingMLUtil.StringToTwips((string)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 = WordprocessingMLUtil.StringToTwips((string)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(WordprocessingMLUtil.StringToTwips((string)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 = WordprocessingMLUtil.StringToTwips((string)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(WordprocessingMLUtil.StringToTwips((string)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 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - twipCounter;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = WordprocessingMLUtil.StringToTwips((string)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 => WordprocessingMLUtil.StringToTwips((string)t.Attribute(W.pos)))
- .LastOrDefault();
- if (lastTabElement != null)
- {
- if (defaultTabStop == 0)
- defaultTabStop = 720;
- var rangeStart = WordprocessingMLUtil.StringToTwips((string)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 => WordprocessingMLUtil.StringToTwips((string)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]), new XAttribute("target","_blank"), 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 base64 = "";
- string altText = "";
- 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);
- base64 = 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";
- base64 = Globals.UnsupportedImage;
- contentType = "image/x-emf";
- altText = "UnsupportedImageError";
- }
- try
- {
- var imageInfo = new ImageInfo()
- {
- ContentType = contentType,
- DrawingElement = element,
- AltText = altText,
- Mathxml = base64
- };
- var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault();
- float? widthInPoints = null;
- float? heightInPoints = null;
- if (style != null)
- {
- var tokens = style.Split(';');
- widthInPoints = WidthInPoints(tokens);
- heightInPoints = HeightInPoints(tokens);
- }
- img = imageHandler(imageInfo);
- //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);
- }
- }
- }
|