1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408 |
- // 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;
- // 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)
- {
- 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);
- }
- }
- 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 defaultTabStop = (int?)sxd.Descendants(W.defaultTabStop).Attributes(W.val).FirstOrDefault() ?? 720;
- var pxd = wordDoc.MainDocumentPart.GetXDocument();
- var root = pxd.Root;
- if (root == null) return;
- var newRoot = (XElement)CalculateSpanWidthTransform(root, defaultTabStop);
- root.ReplaceWith(newRoot);
- wordDoc.MainDocumentPart.PutXDocument();
- }
- // TODO: Refactor. This method is way too long.
- [SuppressMessage("ReSharper", "FunctionComplexityOverflow")]
- private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop)
- {
- var element = node as XElement;
- if (element == null) return node;
- // if it is not a paragraph or if there are no tabs in the paragraph,
- // then no need to continue processing.
- if (element.Name != W.p ||
- !element.DescendantsTrimmed(W.txbxContent).Where(d => d.Name == W.r).Elements(W.tab).Any())
- {
- // TODO: Revisit. Can we just return the node if it is a paragraph that does not have any tab?
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop)));
- }
- var clonedPara = new XElement(element);
- var leftInTwips = 0;
- var firstInTwips = 0;
- var ind = clonedPara.Elements(W.pPr).Elements(W.ind).FirstOrDefault();
- if (ind != null)
- {
- // todo need to handle start and end attributes
- var left = (int?)ind.Attribute(W.left);
- if (left != null)
- leftInTwips = (int)left;
- var firstLine = 0;
- var firstLineAtt = (int?)ind.Attribute(W.firstLine);
- if (firstLineAtt != null)
- firstLine = (int)firstLineAtt;
- var hangingAtt = (int?)ind.Attribute(W.hanging);
- if (hangingAtt != null)
- firstLine = -(int)hangingAtt;
- firstInTwips = leftInTwips + firstLine;
- }
- // calculate the tab stops, in twips
- var tabs = clonedPara
- .Elements(W.pPr)
- .Elements(W.tabs)
- .FirstOrDefault();
- if (tabs == null)
- {
- if (leftInTwips == 0)
- {
- tabs = new XElement(W.tabs,
- Enumerable.Range(1, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- }
- else
- {
- tabs = new XElement(W.tabs,
- new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, leftInTwips)));
- tabs = AddDefaultTabsAfterLastTab(tabs, defaultTabStop);
- }
- }
- else
- {
- if (leftInTwips != 0)
- {
- tabs.Add(
- new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, leftInTwips)));
- }
- tabs = AddDefaultTabsAfterLastTab(tabs, defaultTabStop);
- }
- var twipCounter = firstInTwips;
- var contentToMeasure = element.DescendantsTrimmed(z => z.Name == W.txbxContent || z.Name == W.pPr || z.Name == W.rPr).ToArray();
- var currentElementIdx = 0;
- while (true)
- {
- var currentElement = contentToMeasure[currentElementIdx];
- if (currentElement.Name == W.br)
- {
- twipCounter = leftInTwips;
- currentElement.Add(new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo,
- "{0:0.000}", (decimal)firstInTwips / 1440m)));
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- }
- if (currentElement.Name == W.tab)
- {
- var runContainingTabToReplace = currentElement.Parent;
- var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName") ??
- runContainingTabToReplace.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName");
- var testAmount = twipCounter;
- var tabAfterText = tabs
- .Elements(W.tab)
- .FirstOrDefault(t => (int)t.Attribute(W.pos) > testAmount);
- if (tabAfterText == null)
- {
- // something has gone wrong, so put 1/2 inch in
- if (currentElement.Attribute(PtOpenXml.TabWidth) == null)
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth, 720m));
- break;
- }
- var tabVal = (string)tabAfterText.Attribute(W.val);
- if (tabVal == "right" || tabVal == "end")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- var dummyRun2 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfTextAfterTab - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos), twipCounter + widthOfTextAfterTab);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- if (tabVal == "decimal")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- if (textAfterTab.Contains("."))
- {
- var mantissa = textAfterTab.Split('.')[0];
- var dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, mantissa));
- var widthOfMantissa = CalcWidthOfRunInTwips(dummyRun4);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfMantissa - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- var decims = textAfterTab.Substring(textAfterTab.IndexOf('.'));
- dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, decims));
- var widthOfDecims = CalcWidthOfRunInTwips(dummyRun4);
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos) + widthOfDecims, twipCounter + widthOfMantissa + widthOfDecims);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- else
- {
- var dummyRun2 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - widthOfTextAfterTab - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos), twipCounter + widthOfTextAfterTab);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- }
- if ((string)tabAfterText.Attribute(W.val) == "center")
- {
- var textAfterElements = contentToMeasure
- .Skip(currentElementIdx + 1);
- // take all the content until another tab, br, or cr
- var textElementsToMeasure = textAfterElements
- .TakeWhile(z =>
- z.Name != W.tab &&
- z.Name != W.br &&
- z.Name != W.cr)
- .ToList();
- var textAfterTab = textElementsToMeasure
- .Where(z => z.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate();
- var dummyRun4 = new XElement(W.r,
- fontNameAtt,
- runContainingTabToReplace.Elements(W.rPr),
- new XElement(W.t, textAfterTab));
- var widthOfText = CalcWidthOfRunInTwips(dummyRun4);
- var delta2 = (int)tabAfterText.Attribute(W.pos) - (widthOfText / 2) - twipCounter;
- if (delta2 < 0)
- delta2 = 0;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = Math.Max((int)tabAfterText.Attribute(W.pos) + widthOfText / 2, twipCounter + widthOfText);
- var lastElement = textElementsToMeasure.LastOrDefault();
- if (lastElement == null)
- break; // we're done
- currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- if (tabVal == "left" || tabVal == "start" || tabVal == "num")
- {
- var delta = (int)tabAfterText.Attribute(W.pos) - twipCounter;
- currentElement.Add(
- new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta / 1440m)),
- GetLeader(tabAfterText));
- twipCounter = (int)tabAfterText.Attribute(W.pos);
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- }
- if (currentElement.Name == W.t)
- {
- // TODO: Revisit. This is a quick fix because it doesn't work on Azure.
- // Given the changes we've made elsewhere, though, this is not required
- // for the first tab at least. We could also enhance that other change
- // to deal with all tabs.
- //var runContainingTabToReplace = currentElement.Parent;
- //var paragraphForRun = runContainingTabToReplace.Ancestors(W.p).First();
- //var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.FontName) ??
- // paragraphForRun.Attribute(PtOpenXml.FontName);
- //var languageTypeAtt = runContainingTabToReplace.Attribute(PtOpenXml.LanguageType) ??
- // paragraphForRun.Attribute(PtOpenXml.LanguageType);
- //var dummyRun3 = new XElement(W.r, fontNameAtt, languageTypeAtt,
- // runContainingTabToReplace.Elements(W.rPr),
- // currentElement);
- //var widthOfText = CalcWidthOfRunInTwips(dummyRun3);
- const int widthOfText = 0;
- currentElement.Add(new XAttribute(PtOpenXml.TabWidth,
- string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)widthOfText / 1440m)));
- twipCounter += widthOfText;
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- continue;
- }
- currentElementIdx++;
- if (currentElementIdx >= contentToMeasure.Length)
- break; // we're done
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop)));
- }
- private static XAttribute GetLeader(XElement tabAfterText)
- {
- var leader = (string)tabAfterText.Attribute(W.leader);
- if (leader == null)
- return null;
- return new XAttribute(PtOpenXml.Leader, leader);
- }
- private static XElement AddDefaultTabsAfterLastTab(XElement tabs, int defaultTabStop)
- {
- var lastTabElement = tabs
- .Elements(W.tab)
- .Where(t => (string)t.Attribute(W.val) != "clear" && (string)t.Attribute(W.val) != "bar")
- .OrderBy(t => (int)t.Attribute(W.pos))
- .LastOrDefault();
- if (lastTabElement != null)
- {
- if (defaultTabStop == 0)
- defaultTabStop = 720;
- var rangeStart = (int)lastTabElement.Attribute(W.pos) / defaultTabStop + 1;
- var tempTabs = new XElement(W.tabs,
- tabs.Elements().Where(t => (string)t.Attribute(W.val) != "clear" && (string)t.Attribute(W.val) != "bar"),
- Enumerable.Range(rangeStart, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- tempTabs = new XElement(W.tabs,
- tempTabs.Elements().OrderBy(t => (int)t.Attribute(W.pos)));
- return tempTabs;
- }
- else
- {
- tabs = new XElement(W.tabs,
- Enumerable.Range(1, 100)
- .Select(r => new XElement(W.tab,
- new XAttribute(W.val, "left"),
- new XAttribute(W.pos, r * defaultTabStop))));
- }
- return tabs;
- }
- private static readonly HashSet<string> UnknownFonts = new HashSet<string>();
- private static HashSet<string> _knownFamilies;
- private static HashSet<string> KnownFamilies
- {
- get
- {
- if (_knownFamilies == null)
- {
- _knownFamilies = new HashSet<string>();
- var families = FontFamily.Families;
- foreach (var fam in families)
- _knownFamilies.Add(fam.Name);
- }
- return _knownFamilies;
- }
- }
- private static int CalcWidthOfRunInTwips(XElement r)
- {
- var fontName = (string)r.Attribute(PtOpenXml.pt + "FontName") ??
- (string)r.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName");
- if (fontName == null)
- throw new OpenXmlPowerToolsException("Internal Error, should have FontName attribute");
- if (UnknownFonts.Contains(fontName))
- return 0;
- var rPr = r.Element(W.rPr);
- if (rPr == null)
- throw new OpenXmlPowerToolsException("Internal Error, should have run properties");
- var sz = GetFontSize(r) ?? 22m;
- // unknown font families will throw ArgumentException, in which case just return 0
- if (!KnownFamilies.Contains(fontName))
- return 0;
- // in theory, all unknown fonts are found by the above test, but if not...
- FontFamily ff;
- try
- {
- ff = new FontFamily(fontName);
- }
- catch (ArgumentException)
- {
- UnknownFonts.Add(fontName);
- return 0;
- }
- var fs = FontStyle.Regular;
- if (GetBoolProp(rPr, W.b) || GetBoolProp(rPr, W.bCs))
- fs |= FontStyle.Bold;
- if (GetBoolProp(rPr, W.i) || GetBoolProp(rPr, W.iCs))
- fs |= FontStyle.Italic;
- // Appended blank as a quick fix to accommodate that will get
- // appended to some layout-critical runs such as list item numbers.
- // In some cases, this might not be required or even wrong, so this
- // must be revisited.
- // TODO: Revisit.
- var runText = r.DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.t)
- .Select(t => (string)t)
- .StringConcatenate() + " ";
- var tabLength = r.DescendantsTrimmed(W.txbxContent)
- .Where(e => e.Name == W.tab)
- .Select(t => (decimal)t.Attribute(PtOpenXml.TabWidth))
- .Sum();
- if (runText.Length == 0 && tabLength == 0)
- return 0;
- int multiplier = 1;
- if (runText.Length <= 2)
- multiplier = 100;
- else if (runText.Length <= 4)
- multiplier = 50;
- else if (runText.Length <= 8)
- multiplier = 25;
- else if (runText.Length <= 16)
- multiplier = 12;
- else if (runText.Length <= 32)
- multiplier = 6;
- if (multiplier != 1)
- {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < multiplier; i++)
- sb.Append(runText);
- runText = sb.ToString();
- }
- var w = MetricsGetter.GetTextWidth(ff, fs, sz, runText);
- return (int)(w / 96m * 1440m / multiplier + tabLength * 1440m);
- }
- private static void InsertAppropriateNonbreakingSpaces(WordprocessingDocument wordDoc)
- {
- foreach (var part in wordDoc.ContentParts())
- {
- var pxd = part.GetXDocument();
- var root = pxd.Root;
- if (root == null) return;
- var newRoot = (XElement)InsertAppropriateNonbreakingSpacesTransform(root);
- root.ReplaceWith(newRoot);
- part.PutXDocument();
- }
- }
- // Non-breaking spaces are not required if we use appropriate CSS, i.e., "white-space: pre-wrap;".
- // We only need to make sure that empty w:p elements are translated into non-empty h:p elements,
- // because empty h:p elements would be ignored by browsers.
- // Further, in addition to not being required, non-breaking spaces would change the layout behavior
- // of spans having consecutive spaces. Therefore, avoiding non-breaking spaces has the additional
- // benefit of leading to a more faithful representation of the Word document in HTML.
- private static object InsertAppropriateNonbreakingSpacesTransform(XNode node)
- {
- XElement element = node as XElement;
- if (element != null)
- {
- // child content of run to look for
- // W.br
- // W.cr
- // W.dayLong
- // W.dayShort
- // W.drawing
- // W.monthLong
- // W.monthShort
- // W.noBreakHyphen
- // W.object
- // W.pgNum
- // W.pTab
- // W.separator
- // W.softHyphen
- // W.sym
- // W.t
- // W.tab
- // W.yearLong
- // W.yearShort
- if (element.Name == W.p)
- {
- // Translate empty paragraphs to paragraphs having one run with
- // a normal space. A non-breaking space, i.e., \x00A0, is not
- // required if we use appropriate CSS.
- bool hasContent = element
- .Elements()
- .Where(e => e.Name != W.pPr)
- .DescendantsAndSelf()
- .Any(e =>
- e.Name == W.dayLong ||
- e.Name == W.dayShort ||
- e.Name == W.drawing ||
- e.Name == W.monthLong ||
- e.Name == W.monthShort ||
- e.Name == W.noBreakHyphen ||
- e.Name == W._object ||
- e.Name == W.pgNum ||
- e.Name == W.ptab ||
- e.Name == W.separator ||
- e.Name == W.softHyphen ||
- e.Name == W.sym ||
- e.Name == W.t ||
- e.Name == W.tab ||
- e.Name == W.yearLong ||
- e.Name == W.yearShort
- );
- if (hasContent == false)
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => InsertAppropriateNonbreakingSpacesTransform(n)),
- new XElement(W.r,
- element.Elements(W.pPr).Elements(W.rPr),
- new XElement(W.t, " ")));
- }
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => InsertAppropriateNonbreakingSpacesTransform(n)));
- }
- return node;
- }
- private class SectionAnnotation
- {
- public XElement SectionElement;
- }
- private static void AnnotateForSections(WordprocessingDocument wordDoc)
- {
- var xd = wordDoc.MainDocumentPart.GetXDocument();
- var document = xd.Root;
- if (document == null) return;
- var body = document.Element(W.body);
- if (body == null) return;
- // move last sectPr into last paragraph
- var lastSectPr = body.Elements(W.sectPr).LastOrDefault();
- if (lastSectPr != null)
- {
- // if the last thing in the document is a table, Word will always insert a paragraph following that.
- var lastPara = body
- .DescendantsTrimmed(W.txbxContent)
- .LastOrDefault(p => p.Name == W.p);
- if (lastPara != null)
- {
- var lastParaProps = lastPara.Element(W.pPr);
- if (lastParaProps != null)
- lastParaProps.Add(lastSectPr);
- else
- lastPara.Add(new XElement(W.pPr, lastSectPr));
- lastSectPr.Remove();
- }
- }
- var reverseDescendants = xd.Descendants().Reverse().ToList();
- var currentSection = InitializeSectionAnnotation(reverseDescendants);
- foreach (var d in reverseDescendants)
- {
- if (d.Name == W.sectPr)
- {
- if (d.Attribute(XNamespace.Xmlns + "w") == null)
- d.Add(new XAttribute(XNamespace.Xmlns + "w", W.w));
- currentSection = new SectionAnnotation()
- {
- SectionElement = d
- };
- }
- else
- d.AddAnnotation(currentSection);
- }
- }
- private static SectionAnnotation InitializeSectionAnnotation(IEnumerable<XElement> reverseDescendants)
- {
- var currentSection = new SectionAnnotation()
- {
- SectionElement = reverseDescendants.FirstOrDefault(e => e.Name == W.sectPr)
- };
- if (currentSection.SectionElement != null &&
- currentSection.SectionElement.Attribute(XNamespace.Xmlns + "w") == null)
- currentSection.SectionElement.Add(new XAttribute(XNamespace.Xmlns + "w", W.w));
- // todo what should the default section props be?
- if (currentSection.SectionElement == null)
- currentSection = new SectionAnnotation()
- {
- SectionElement = new XElement(W.sectPr,
- new XAttribute(XNamespace.Xmlns + "w", W.w),
- new XElement(W.pgSz,
- new XAttribute(W._w, 12240),
- new XAttribute(W.h, 15840)),
- new XElement(W.pgMar,
- new XAttribute(W.top, 1440),
- new XAttribute(W.right, 1440),
- new XAttribute(W.bottom, 1440),
- new XAttribute(W.left, 1440),
- new XAttribute(W.header, 720),
- new XAttribute(W.footer, 720),
- new XAttribute(W.gutter, 0)),
- new XElement(W.cols,
- new XAttribute(W.space, 720)),
- new XElement(W.docGrid,
- new XAttribute(W.linePitch, 360)))
- };
- return currentSection;
- }
- private static object CreateBorderDivs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, IEnumerable<XElement> elements)
- {
- return elements.GroupAdjacent(e =>
- {
- var pBdr = e.Elements(W.pPr).Elements(W.pBdr).FirstOrDefault();
- if (pBdr != null)
- {
- var indStr = string.Empty;
- var ind = e.Elements(W.pPr).Elements(W.ind).FirstOrDefault();
- if (ind != null)
- indStr = ind.ToString(SaveOptions.DisableFormatting);
- return pBdr.ToString(SaveOptions.DisableFormatting) + indStr;
- }
- return e.Name == W.tbl ? "table" : string.Empty;
- })
- .Select(g =>
- {
- if (g.Key == string.Empty)
- {
- return (object)GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m);
- }
- if (g.Key == "table")
- {
- return g.Select(gc => ConvertToHtmlTransform(wordDoc, settings, gc, false, 0));
- }
- var pPr = g.First().Elements(W.pPr).First();
- var pBdr = pPr.Element(W.pBdr);
- var style = new Dictionary<string, string>();
- GenerateBorderStyle(pBdr, W.top, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.right, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.bottom, style, BorderType.Paragraph);
- GenerateBorderStyle(pBdr, W.left, style, BorderType.Paragraph);
- var currentMarginLeft = 0m;
- var ind = pPr.Element(W.ind);
- if (ind != null)
- {
- var leftInInches = (decimal?)ind.Attribute(W.left) / 1440m ?? 0;
- var hangingInInches = -(decimal?)ind.Attribute(W.hanging) / 1440m ?? 0;
- currentMarginLeft = leftInInches + hangingInInches;
- style.AddIfMissing("margin-left",
- currentMarginLeft > 0m
- ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", currentMarginLeft)
- : "0");
- }
- var div = new XElement(Xhtml.div,
- GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft));
- div.AddAnnotation(style);
- return div;
- })
- .ToList();
- }
- private static IEnumerable<object> GroupAndVerticallySpaceNumberedParagraphs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- IEnumerable<XElement> elements, decimal currentMarginLeft)
- {
- var grouped = elements
- .GroupAdjacent(e =>
- {
- var abstractNumId = (string)e.Attribute(PtOpenXml.pt + "AbstractNumId");
- if (abstractNumId != null)
- return "num:" + abstractNumId;
- var contextualSpacing = e.Elements(W.pPr).Elements(W.contextualSpacing).FirstOrDefault();
- if (contextualSpacing != null)
- {
- var styleName = (string)e.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault();
- if (styleName == null)
- return "";
- return "sty:" + styleName;
- }
- return "";
- })
- .ToList();
- var newContent = grouped
- .Select(g =>
- {
- if (g.Key == "")
- return g.Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
- var last = g.Count() - 1;
- return g.Select((e, i) => ConvertToHtmlTransform(wordDoc, settings, e, i != last, currentMarginLeft));
- });
- return (IEnumerable<object>)newContent;
- }
- private class BorderMappingInfo
- {
- public string CssName;
- public decimal CssSize;
- }
- private static readonly Dictionary<string, BorderMappingInfo> BorderStyleMap = new Dictionary<string, BorderMappingInfo>()
- {
- { "single", new BorderMappingInfo() { CssName = "solid", CssSize = 1.0m }},
- { "dotted", new BorderMappingInfo() { CssName = "dotted", CssSize = 1.0m }},
- { "dashSmallGap", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dashed", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "dotDotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }},
- { "double", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }},
- { "triple", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }},
- { "thinThickSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }},
- { "thickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }},
- { "thinThickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thinThickMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }},
- { "thinThickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }},
- { "thinThickLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "thickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "thinThickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }},
- { "wave", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }},
- { "doubleWave", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }},
- { "dashDotStroked", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }},
- { "threeDEmboss", new BorderMappingInfo() { CssName = "ridge", CssSize = 6.0m }},
- { "threeDEngrave", new BorderMappingInfo() { CssName = "groove", CssSize = 6.0m }},
- { "outset", new BorderMappingInfo() { CssName = "outset", CssSize = 4.5m }},
- { "inset", new BorderMappingInfo() { CssName = "inset", CssSize = 4.5m }},
- };
- private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictionary<string, string> style, BorderType borderType)
- {
- string whichSide;
- if (sideXName == W.top)
- whichSide = "top";
- else if (sideXName == W.right)
- whichSide = "right";
- else if (sideXName == W.bottom)
- whichSide = "bottom";
- else
- whichSide = "left";
- if (pBdr == null)
- {
- style.Add("border-" + whichSide, "none");
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- style.Add("padding-" + whichSide, "5.4pt");
- return;
- }
- var side = pBdr.Element(sideXName);
- if (side == null)
- {
- style.Add("border-" + whichSide, "none");
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- style.Add("padding-" + whichSide, "5.4pt");
- return;
- }
- var type = (string)side.Attribute(W.val);
- if (type == "nil" || type == "none")
- {
- style.Add("border-" + whichSide + "-style", "none");
- var space = (decimal?)side.Attribute(W.space) ?? 0;
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- if (space < 5.4m)
- space = 5.4m;
- style.Add("padding-" + whichSide,
- space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space));
- }
- else
- {
- var sz = (int)side.Attribute(W.sz);
- var space = (decimal?)side.Attribute(W.space) ?? 0;
- var color = (string)side.Attribute(W.color);
- if (color == null || color == "auto")
- color = "windowtext";
- else
- color = ConvertColor(color);
- decimal borderWidthInPoints = Math.Max(1m, Math.Min(96m, Math.Max(2m, sz)) / 8m);
- var borderStyle = "solid";
- if (BorderStyleMap.ContainsKey(type))
- {
- var borderInfo = BorderStyleMap[type];
- borderStyle = borderInfo.CssName;
- if (type == "double")
- {
- if (sz <= 8)
- borderWidthInPoints = 2.5m;
- else if (sz <= 18)
- borderWidthInPoints = 6.75m;
- else
- borderWidthInPoints = sz / 3m;
- }
- else if (type == "triple")
- {
- if (sz <= 8)
- borderWidthInPoints = 8m;
- else if (sz <= 18)
- borderWidthInPoints = 11.25m;
- else
- borderWidthInPoints = 11.25m;
- }
- else if (type.ToLower().Contains("dash"))
- {
- if (sz <= 4)
- borderWidthInPoints = 1m;
- else if (sz <= 12)
- borderWidthInPoints = 1.5m;
- else
- borderWidthInPoints = 2m;
- }
- else if (type != "single")
- borderWidthInPoints = borderInfo.CssSize;
- }
- if (type == "outset" || type == "inset")
- color = "";
- var borderWidth = string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", borderWidthInPoints);
- style.Add("border-" + whichSide, borderStyle + " " + color + " " + borderWidth);
- if (borderType == BorderType.Cell &&
- (whichSide == "left" || whichSide == "right"))
- if (space < 5.4m)
- space = 5.4m;
- style.Add("padding-" + whichSide,
- space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space));
- }
- }
- private static readonly Dictionary<string, Func<string, string, string>> ShadeMapper = new Dictionary<string, Func<string, string, string>>()
- {
- { "auto", (c, f) => c },
- { "clear", (c, f) => f },
- { "nil", (c, f) => f },
- { "solid", (c, f) => c },
- { "diagCross", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "diagStripe", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "horzCross", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "horzStripe", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "pct10", (c, f) => ConvertColorFillPct(c, f, .1) },
- { "pct12", (c, f) => ConvertColorFillPct(c, f, .125) },
- { "pct15", (c, f) => ConvertColorFillPct(c, f, .15) },
- { "pct20", (c, f) => ConvertColorFillPct(c, f, .2) },
- { "pct25", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "pct30", (c, f) => ConvertColorFillPct(c, f, .3) },
- { "pct35", (c, f) => ConvertColorFillPct(c, f, .35) },
- { "pct37", (c, f) => ConvertColorFillPct(c, f, .375) },
- { "pct40", (c, f) => ConvertColorFillPct(c, f, .4) },
- { "pct45", (c, f) => ConvertColorFillPct(c, f, .45) },
- { "pct50", (c, f) => ConvertColorFillPct(c, f, .50) },
- { "pct55", (c, f) => ConvertColorFillPct(c, f, .55) },
- { "pct60", (c, f) => ConvertColorFillPct(c, f, .60) },
- { "pct62", (c, f) => ConvertColorFillPct(c, f, .625) },
- { "pct65", (c, f) => ConvertColorFillPct(c, f, .65) },
- { "pct70", (c, f) => ConvertColorFillPct(c, f, .7) },
- { "pct75", (c, f) => ConvertColorFillPct(c, f, .75) },
- { "pct80", (c, f) => ConvertColorFillPct(c, f, .8) },
- { "pct85", (c, f) => ConvertColorFillPct(c, f, .85) },
- { "pct87", (c, f) => ConvertColorFillPct(c, f, .875) },
- { "pct90", (c, f) => ConvertColorFillPct(c, f, .9) },
- { "pct95", (c, f) => ConvertColorFillPct(c, f, .95) },
- { "reverseDiagStripe", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "thinDiagCross", (c, f) => ConvertColorFillPct(c, f, .5) },
- { "thinDiagStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinHorzCross", (c, f) => ConvertColorFillPct(c, f, .3) },
- { "thinHorzStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinReverseDiagStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- { "thinVertStripe", (c, f) => ConvertColorFillPct(c, f, .25) },
- };
- private static readonly Dictionary<string, string> ShadeCache = new Dictionary<string, string>();
- // fill is the background, color is the foreground
- private static string ConvertColorFillPct(string color, string fill, double pct)
- {
- if (color == "auto")
- color = "000000";
- if (fill == "auto")
- fill = "ffffff";
- var key = color + fill + pct.ToString(CultureInfo.InvariantCulture);
- if (ShadeCache.ContainsKey(key))
- return ShadeCache[key];
- var fillRed = Convert.ToInt32(fill.Substring(0, 2), 16);
- var fillGreen = Convert.ToInt32(fill.Substring(2, 2), 16);
- var fillBlue = Convert.ToInt32(fill.Substring(4, 2), 16);
- var colorRed = Convert.ToInt32(color.Substring(0, 2), 16);
- var colorGreen = Convert.ToInt32(color.Substring(2, 2), 16);
- var colorBlue = Convert.ToInt32(color.Substring(4, 2), 16);
- var finalRed = (int)(fillRed - (fillRed - colorRed) * pct);
- var finalGreen = (int)(fillGreen - (fillGreen - colorGreen) * pct);
- var finalBlue = (int)(fillBlue - (fillBlue - colorBlue) * pct);
- var returnValue = string.Format("{0:x2}{1:x2}{2:x2}", finalRed, finalGreen, finalBlue);
- ShadeCache.Add(key, returnValue);
- return returnValue;
- }
- private static void CreateStyleFromShd(Dictionary<string, string> style, XElement shd)
- {
- if (shd == null)
- return;
- var shadeType = (string)shd.Attribute(W.val);
- var color = (string)shd.Attribute(W.color);
- var fill = (string)shd.Attribute(W.fill);
- if (ShadeMapper.ContainsKey(shadeType))
- {
- color = ShadeMapper[shadeType](color, fill);
- }
- if (color != null)
- {
- var cvtColor = ConvertColor(color);
- if (!string.IsNullOrEmpty(cvtColor))
- style.AddIfMissing("background", cvtColor);
- }
- }
- private static readonly Dictionary<string, string> NamedColors = new Dictionary<string, string>()
- {
- {"black", "black"},
- {"blue", "blue" },
- {"cyan", "aqua" },
- {"green", "green" },
- {"magenta", "fuchsia" },
- {"red", "red" },
- {"yellow", "yellow" },
- {"white", "white" },
- {"darkBlue", "#00008B" },
- {"darkCyan", "#008B8B" },
- {"darkGreen", "#006400" },
- {"darkMagenta", "#800080" },
- {"darkRed", "#8B0000" },
- {"darkYellow", "#808000" },
- {"darkGray", "#A9A9A9" },
- {"lightGray", "#D3D3D3" },
- {"none", "" },
- };
- private static void CreateColorProperty(string propertyName, string color, Dictionary<string, string> style)
- {
- if (color == null)
- return;
- // "auto" color is black for "color" and white for "background" property.
- if (color == "auto")
- color = propertyName == "color" ? "black" : "white";
- if (NamedColors.ContainsKey(color))
- {
- var lc = NamedColors[color];
- if (lc == "")
- return;
- style.AddIfMissing(propertyName, lc);
- return;
- }
- style.AddIfMissing(propertyName, "#" + color);
- }
- private static string ConvertColor(string color)
- {
- // "auto" color is black for "color" and white for "background" property.
- // As this method is only called for "background" colors, "auto" is translated
- // to "white" and never "black".
- if (color == "auto")
- color = "white";
- if (NamedColors.ContainsKey(color))
- {
- var lc = NamedColors[color];
- if (lc == "")
- return "black";
- return lc;
- }
- return "#" + color;
- }
- private static readonly Dictionary<string, string> FontFallback = new Dictionary<string, string>()
- {
- { "Arial", @"'{0}', 'sans-serif'" },
- { "Arial Narrow", @"'{0}', 'sans-serif'" },
- { "Arial Rounded MT Bold", @"'{0}', 'sans-serif'" },
- { "Arial Unicode MS", @"'{0}', 'sans-serif'" },
- { "Baskerville Old Face", @"'{0}', 'serif'" },
- { "Berlin Sans FB", @"'{0}', 'sans-serif'" },
- { "Berlin Sans FB Demi", @"'{0}', 'sans-serif'" },
- { "Calibri Light", @"'{0}', 'sans-serif'" },
- { "Gill Sans MT", @"'{0}', 'sans-serif'" },
- { "Gill Sans MT Condensed", @"'{0}', 'sans-serif'" },
- { "Lucida Sans", @"'{0}', 'sans-serif'" },
- { "Lucida Sans Unicode", @"'{0}', 'sans-serif'" },
- { "Segoe UI", @"'{0}', 'sans-serif'" },
- { "Segoe UI Light", @"'{0}', 'sans-serif'" },
- { "Segoe UI Semibold", @"'{0}', 'sans-serif'" },
- { "Tahoma", @"'{0}', 'sans-serif'" },
- { "Trebuchet MS", @"'{0}', 'sans-serif'" },
- { "Verdana", @"'{0}', 'sans-serif'" },
- { "Book Antiqua", @"'{0}', 'serif'" },
- { "Bookman Old Style", @"'{0}', 'serif'" },
- { "Californian FB", @"'{0}', 'serif'" },
- { "Cambria", @"'{0}', 'serif'" },
- { "Constantia", @"'{0}', 'serif'" },
- { "Garamond", @"'{0}', 'serif'" },
- { "Lucida Bright", @"'{0}', 'serif'" },
- { "Lucida Fax", @"'{0}', 'serif'" },
- { "Palatino Linotype", @"'{0}', 'serif'" },
- { "Times New Roman", @"'{0}', 'serif'" },
- { "Wide Latin", @"'{0}', 'serif'" },
- { "Courier New", @"'{0}'" },
- { "Lucida Console", @"'{0}'" },
- };
- private static void CreateFontCssProperty(string font, Dictionary<string, string> style)
- {
- if (FontFallback.ContainsKey(font))
- {
- style.AddIfMissing("font-family", string.Format(FontFallback[font], font));
- return;
- }
- style.AddIfMissing("font-family", font);
- }
- private static bool GetBoolProp(XElement runProps, XName xName)
- {
- var p = runProps.Element(xName);
- if (p == null)
- return false;
- var v = p.Attribute(W.val);
- if (v == null)
- return true;
- var s = v.Value.ToLower();
- if (s == "0" || s == "false")
- return false;
- if (s == "1" || s == "true")
- return true;
- return false;
- }
- private static object ConvertContentThatCanContainFields(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings,
- IEnumerable<XElement> elements)
- {
- var grouped = elements
- .GroupAdjacent(e =>
- {
- var stack = e.Annotation<Stack<FieldRetriever.FieldElementTypeInfo>>();
- return stack == null || !stack.Any() ? (int?)null : stack.Select(st => st.Id).Min();
- })
- .ToList();
- var txformed = grouped
- .Select(g =>
- {
- var key = g.Key;
- if (key == null)
- return (object)g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m));
- var instrText = FieldRetriever.InstrText(g.First().Ancestors().Last(), (int)key)
- .TrimStart('{').TrimEnd('}');
- var parsed = FieldRetriever.ParseField(instrText);
- if (parsed.FieldType != "HYPERLINK")
- return g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m));
- var content = g.DescendantsAndSelf(W.r).Select(run => ConvertRun(wordDoc, settings, run));
- var a = parsed.Arguments.Length > 0
- ? new XElement(Xhtml.a, new XAttribute("href", parsed.Arguments[0]), content)
- : new XElement(Xhtml.a, content);
- var a2 = a as XElement;
- if (!a2.Nodes().Any())
- {
- a2.Add(new XText(""));
- return a2;
- }
- return a;
- })
- .ToList();
- return txformed;
- }
- #region Image Processing
- // Don't process wmf files (with contentType == "image/x-wmf") because GDI consumes huge amounts
- // of memory when dealing with wmf perhaps because it loads a DLL to do the rendering?
- // It actually works, but is not recommended.
- public static readonly List<string> ImageContentTypes = new List<string>
- {
- "image/png", "image/gif", "image/tiff", "image/jpeg","image/x-wmf","image/x-emf","application/vnd.openxmlformats-officedocument.oleObject"
- };
- public static XElement ProcessImage(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- if (imageHandler == null)
- {
- return null;
- }
- if (element.Name == W.drawing)
- {
- return ProcessDrawing(wordDoc, element, imageHandler);
- }
- if (element.Name == W.pict || element.Name == W._object)
- {
- return ProcessPictureOrObject(wordDoc, element, imageHandler);
- }
- return null;
- }
- private static XElement ProcessDrawing(WordprocessingDocument wordDoc,
- XElement element, Func<ImageInfo, XElement> imageHandler)
- {
- var containerElement = element.Elements()
- .FirstOrDefault(e => e.Name == WP.inline || e.Name == WP.anchor);
- if (containerElement == null) return null;
- string hyperlinkUri = null;
- var hyperlinkElement = element
- .Elements(WP.inline)
- .Elements(WP.docPr)
- .Elements(A.hlinkClick)
- .FirstOrDefault();
- if (hyperlinkElement != null)
- {
- var rId = (string)hyperlinkElement.Attribute(R.id);
- if (rId != null)
- {
- var hyperlinkRel = wordDoc.MainDocumentPart.HyperlinkRelationships.FirstOrDefault(hlr => hlr.Id == rId);
- if (hyperlinkRel != null)
- {
- hyperlinkUri = hyperlinkRel.Uri.ToString();
- }
- }
- }
- var extentCx = (int?)containerElement.Elements(WP.extent)
- .Attributes(NoNamespace.cx).FirstOrDefault();
- var extentCy = (int?)containerElement.Elements(WP.extent)
- .Attributes(NoNamespace.cy).FirstOrDefault();
- var altText = (string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.descr).FirstOrDefault() ??
- ((string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.name).FirstOrDefault() ?? "");
- if (containerElement.Elements(A.graphic)
- .Elements(A.graphicData) != null)
- {
- XElement rest = null;
- var elms = containerElement.Elements(A.graphic)
- .Elements(A.graphicData).Elements();
- foreach (var elm in elms)
- {
- if (elm.Name == Pic._pic)
- {
- rest = ProcessPic(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- if (elm.Name == C.chart)
- {
- rest = ProcessChart(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- if (elm.Name == DGM.relIds)
- {
- rest = ProcessDgm(wordDoc, containerElement, imageHandler, extentCx, extentCy, altText, elm, hyperlinkUri);
- break;
- }
- }
- return rest;
- }
- else
- {
- return null;
- }
- }
- public static XElement ProcessPic(WordprocessingDocument wordDoc, XElement containerElement, Func<ImageInfo, XElement> imageHandler,
- int? extentCx, int? extentCy, string altText, XElement element, string hyperlinkUri)
- {
- var blipFill = containerElement.Elements(A.graphic)
- .Elements(A.graphicData)
- .Elements(Pic._pic).Elements(Pic.blipFill).FirstOrDefault();
- if (blipFill == null) return null;
- var imageRid = (string)blipFill.Elements(A.blip).Attributes(R.embed).FirstOrDefault();
- if (imageRid == null) return null;
- var pp3 = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp => pp.RelationshipId == imageRid);
- if (pp3 == null) return null;
- var imagePart = (ImagePart)pp3.OpenXmlPart;
- if (imagePart == null) return null;
- // If the image markup points to a NULL image, then following will throw an ArgumentOutOfRangeException
- try
- {
- imagePart = (ImagePart)wordDoc.MainDocumentPart.GetPartById(imageRid);
- }
- catch (ArgumentOutOfRangeException)
- {
- return null;
- }
- var contentType = imagePart.ContentType;
- if (!ImageContentTypes.Contains(contentType))
- return null;
- var uil = imagePart.Uri;
- //WORDHelper.GetWordNodeByPath(wordDoc,"");
- var Stream = imagePart.GetStream();
- try
- {
- if (contentType == "image/x-wmf" || contentType == "image/x-emf")
- {
- return ProcessWmf(Stream, contentType, element, hyperlinkUri, imageHandler);
- }
- else
- {
- using (var bitmap = new Bitmap(Stream))
- {
- if (extentCx != null && extentCy != null)
- {
- var imageInfo = new ImageInfo()
- {
- Bitmap = bitmap,
- ImgStyleAttribute = new XAttribute("style",
- string.Format(NumberFormatInfo.InvariantInfo,
- "width: {0}px; ",
- // "height: {1}in",
- (float)extentCx * 96.0 / (float)ImageInfo.EmusPerInch
- // ,
- //(float)extentCy*96.0 / (float)ImageInfo.EmusPerInch
- )),
- ContentType = contentType,
- DrawingElement = element,
- AltText = altText,
- };
- var imgElement2 = imageHandler(imageInfo);
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- imgElement2);
- }
- return imgElement2;
- }
- var imageInfo2 = new ImageInfo()
- {
- Bitmap = bitmap,
- ContentType = contentType,
- DrawingElement = element,
- AltText = altText,
- };
- var imgElement = imageHandler(imageInfo2);
- if (hyperlinkUri != null)
- {
- return new XElement(XhtmlNoNamespace.a,
- new XAttribute(XhtmlNoNamespace.href, hyperlinkUri),
- imgElement);
- }
- return imgElement;
- }
- }
- }
- catch
- {
- XElement img = null;
- byte[] bytes = new byte[Stream.Length];
- Stream.Read(bytes, 0, bytes.Length);
- // 设置当前流的位置为流的开始
- Stream.Seek(0, SeekOrigin.Begin);
- string url = System.Convert.ToBase64String(bytes);
- img = new XElement(Xhtml.img,
- new XAttribute(NoNamespace.src, "data:" + contentType + ";base64," + url),
- new XAttribute(NoNamespace.alt, "wrong image"));
- return img;
- }
- finally
- {
- Stream.Close();
- }
- }
- public static XElement ProcessWmf(Stream stream, string contentType, XElement element, string hyperlinkUri, Func<ImageInfo, XElement> imageHandler)
- {
- XElement img = null;
- string 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);
- }
- }
- }
|