123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- // 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.Globalization;
- using System.Linq;
- using System.Text;
- using System.Xml.Linq;
- using DocumentFormat.OpenXml.Packaging;
- using System.IO;
- namespace OpenXmlPowerTools
- {
- public partial class SmlDocument
- {
- [SuppressMessage("ReSharper", "UnusedMember.Global")]
- public XElement ConvertToHtml(SmlToHtmlConverterSettings htmlConverterSettings, string tableName)
- {
- return SmlToHtmlConverter.ConvertTableToHtml(this, htmlConverterSettings, tableName);
- }
- [SuppressMessage("ReSharper", "UnusedMember.Global")]
- public XElement ConvertTableToHtml(string tableName)
- {
- SmlToHtmlConverterSettings settings = new SmlToHtmlConverterSettings();
- return SmlToHtmlConverter.ConvertTableToHtml(this, settings, tableName);
- }
- }
- [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
- public class SmlToHtmlConverterSettings
- {
- public string PageTitle;
- public string CssClassPrefix;
- public bool FabricateCssClasses;
- public string GeneralCss;
- public string AdditionalCss;
- public SmlToHtmlConverterSettings()
- {
- PageTitle = "";
- CssClassPrefix = "pt-";
- FabricateCssClasses = true;
- GeneralCss = "span { white-space: pre-wrap; }";
- AdditionalCss = "";
- }
- public SmlToHtmlConverterSettings(SmlToHtmlConverterSettings htmlConverterSettings)
- {
- PageTitle = htmlConverterSettings.PageTitle;
- CssClassPrefix = htmlConverterSettings.CssClassPrefix;
- FabricateCssClasses = htmlConverterSettings.FabricateCssClasses;
- GeneralCss = htmlConverterSettings.GeneralCss;
- AdditionalCss = htmlConverterSettings.AdditionalCss;
- }
- }
- public static class SmlToHtmlConverter
- {
- // ***********************************************************************************************************************************
- #region PublicApis
- public static XElement ConvertTableToHtml(SmlDocument smlDoc, SmlToHtmlConverterSettings settings, string tableName)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- ms.Write(smlDoc.DocumentByteArray, 0, smlDoc.DocumentByteArray.Length);
- using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(ms, false))
- {
- var rangeXml = SmlDataRetriever.RetrieveTable(sDoc, tableName);
- var xhtml = SmlToHtmlConverter.ConvertToHtmlInternal(sDoc, settings, rangeXml);
- return xhtml;
- }
- }
- }
- public static XElement ConvertTableToHtml(SpreadsheetDocument sDoc, SmlToHtmlConverterSettings settings, string tableName)
- {
- var rangeXml = SmlDataRetriever.RetrieveTable(sDoc, tableName);
- var xhtml = SmlToHtmlConverter.ConvertToHtmlInternal(sDoc, settings, rangeXml);
- return xhtml;
- }
- #endregion
- // ***********************************************************************************************************************************
- private static XElement ConvertToHtmlInternal(SpreadsheetDocument sDoc, SmlToHtmlConverterSettings htmlConverterSettings, XElement rangeXml)
- {
- XElement xhtml = (XElement)ConvertToHtmlTransform(sDoc, htmlConverterSettings, rangeXml);
- 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 XNode ConvertToHtmlTransform(SpreadsheetDocument sDoc, SmlToHtmlConverterSettings htmlConverterSettings, XNode node)
- {
- var element = node as XElement;
- if (element != null)
- {
- return new XElement(element.Name,
- element.Attributes(),
- element.Nodes().Select(n => ConvertToHtmlTransform(sDoc, htmlConverterSettings, n)));
- }
- return node;
- }
- private static void ReifyStylesAndClasses(SmlToHtmlConverterSettings 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);
- }
- }
- }
- 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)
- {
- // Ignore element.
- return null;
- }
- 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 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;
- }
- }
- }
|