コード例 #1
0
        // ---------------------------------------------------------------------
        //
        // Internal Methods
        //
        // ---------------------------------------------------------------------

        /// <summary>
        /// Main entry point for Xaml-to-Html converter.
        /// Converts a xaml string into html string.
        /// </summary>
        /// <param name="xamlString">
        /// Xaml strinng to convert.
        /// </param>
        /// <param name="context">Conversion context</param>
        /// <returns>
        /// Html string produced from a source xaml.
        /// </returns>
        public static string ConvertXamlToHtml(string xamlString, HtmlFromXamlContext context)
        {
            using (var xamlReader = XmlReader.Create(new StringReader(xamlString), new XmlReaderSettings {
                DtdProcessing = DtdProcessing.Ignore, IgnoreWhitespace = true
            }))
            {
                Preprocess(xamlReader, context);
            }

            using (var xamlReader = XmlReader.Create(new StringReader(xamlString), new XmlReaderSettings {
                DtdProcessing = DtdProcessing.Ignore, IgnoreWhitespace = true
            }))
            {
                var htmlStringBuilder = new StringBuilder(256);
                using (var sw = new StringWriter(htmlStringBuilder))
                {
                    using (var htmlWriter = XmlWriter.Create(sw, new XmlWriterSettings {
                        OmitXmlDeclaration = true
                    }))
                    {
                        if (!WriteFlowDocument(xamlReader, htmlWriter, context))
                        {
                            return(string.Empty);
                        }
                    }
                }
                return(htmlStringBuilder.ToString());
            }
        }
コード例 #2
0
        // ---------------------------------------------------------------------
        //
        // Private Methods
        //
        // ---------------------------------------------------------------------

        #region Private Methods

        private static void Preprocess(XmlReader xamlReader, HtmlFromXamlContext context)
        {
            if (!xamlReader.IsEmptyElement)
            {
                while (ReadNextToken(xamlReader) && xamlReader.NodeType != XmlNodeType.EndElement)
                {
                    if (xamlReader.NodeType == XmlNodeType.Element)
                    {
                        if (xamlReader.Name == "Table")
                        {
                            context.AddTable();
                        }
                        else if (xamlReader.Name == "TableCell")
                        {
                            while (xamlReader.MoveToNextAttribute())
                            {
                                if (xamlReader.Name == "BorderThickness")
                                {
                                    context.CurrentTable.AddBorder(GetThickness(xamlReader.Value));
                                }
                            }
                            xamlReader.MoveToElement();
                        }

                        Preprocess(xamlReader, context);
                    }
                }

                //				Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement);
            }
        }
コード例 #3
0
        private static void WriteElementWithContent(XmlReader xamlReader, XmlWriter htmlWriter, string htmlElementName, StringBuilder inlineStyle, HtmlFromXamlContext context)
        {
            var subElements = new List <string>();

            var elementName = xamlReader.LocalName;

            if (!string.IsNullOrEmpty(elementName))
            {
                context.AddReaderElement(elementName);
            }

            if (!string.IsNullOrEmpty(htmlElementName))
            {
                htmlWriter.WriteStartElement(htmlElementName);
            }

            WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle, subElements, context);

            foreach (var element in subElements)
            {
                htmlWriter.WriteStartElement(element);
            }

            WriteElementContent(xamlReader, string.IsNullOrEmpty(htmlElementName) ? null : htmlWriter, inlineStyle, context);

            foreach (var element in subElements)
            {
                htmlWriter.WriteEndElement();
            }

            if (!string.IsNullOrEmpty(htmlElementName))
            {
                htmlWriter.WriteEndElement();
            }

            if (!string.IsNullOrEmpty(elementName))
            {
                context.RemoveReaderElement(elementName);
            }
        }
コード例 #4
0
        /// <summary>
        /// Converts a xaml element into an appropriate html element.
        /// </summary>
        /// <param name="xamlReader">
        /// On entry this XTextReader must be on Element start tag;
        /// on exit - on EndElement tag.
        /// </param>
        /// <param name="htmlWriter">
        /// May be null, in which case we are skipping xaml content
        /// without producing any html output
        /// </param>
        /// <param name="inlineStyle">
        /// StringBuilder used for collecting css properties for inline STYLE attributes on every level.
        /// </param>
        /// <param name="context">Conversion context</param>
        private static void WriteElement(XmlReader xamlReader, XmlWriter htmlWriter, StringBuilder inlineStyle, HtmlFromXamlContext context)
        {
            Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);

            if (htmlWriter == null)
            {
                // Skipping mode; recurse into the xaml element without any output
                WriteElementContent(xamlReader, /*htmlWriter:*/ null, null, context);
            }
            else
            {
                string htmlElementName = null;

                switch (xamlReader.LocalName)
                {
                case "Run":
                case "Span":
                    htmlElementName = "span";
                    break;

                case "InlineUIContainer":
                    htmlElementName = "span";
                    break;

                case "Bold":
                    htmlElementName = "b";
                    break;

                case "Italic":
                    htmlElementName = "i";
                    break;

                case "Paragraph":
                    htmlElementName = "p";
                    break;

                case "BlockUIContainer":
                    htmlElementName = "div";
                    break;

                case "Section":
                    htmlElementName = "div";
                    break;

                case "Table":
                    htmlElementName = "table";
                    context.TableMove();
                    break;

                case "Table.Columns":
                    htmlElementName = "colgroup";
                    break;

                case "TableColumn":
                    htmlElementName = "col";
                    break;

                case "TableRowGroup":
                    htmlElementName = "tbody";
                    break;

                case "TableRow":
                    htmlElementName = "tr";
                    break;

                case "TableCell":
                    htmlElementName = "td";
                    break;

                case "List":
                    string marker = xamlReader.GetAttribute("MarkerStyle");
                    if (marker == null || marker == "None" || marker == "Disc" || marker == "Circle" || marker == "Square" || marker == "Box")
                    {
                        htmlElementName = "ul";
                    }
                    else
                    {
                        htmlElementName = "ol";
                    }
                    break;

                case "ListItem":
                    htmlElementName = "li";
                    break;

                default:
                    if (context.OnGetHtmlElementName != null)
                    {
                        // Custom handling of the element
                        htmlElementName = context.OnGetHtmlElementName(xamlReader.LocalName);
                    }
                    else
                    {
                        htmlElementName = null;
                    }
                    break;
                }

                if (htmlWriter != null && htmlElementName != null)
                {
                    WriteElementWithContent(xamlReader, htmlWriter, htmlElementName, inlineStyle, context);
                }
                else
                {
                    // Skip this unrecognized xaml element
                    WriteElementContent(xamlReader, /*htmlWriter:*/ null, null, context);
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Conberts an element notation of complex property into
        /// </summary>
        /// <param name="xamlReader">
        /// On entry this XTextReader must be on Element start tag;
        /// on exit - on EndElement tag.
        /// </param>
        /// <param name="htmlWriter">
        /// May be null, in which case we are skipping xaml content
        /// without producing any html output
        /// </param>
        /// <param name="inlineStyle">
        /// StringBuilder containing a value for STYLE attribute.
        /// </param>
        /// <param name="context">Conversion context</param>
        private static bool HandleComplexProperty(XmlReader xamlReader, XmlWriter htmlWriter, StringBuilder inlineStyle, HtmlFromXamlContext context)
        {
            Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);

            // ship Table.Columns (unhandled)
            if (!xamlReader.Name.Contains(".") || xamlReader.Name == "Table.Columns")
            {
                return(false);
            }

            if (xamlReader.Name.EndsWith(".TextDecorations"))
            {
                var level       = 1;
                var decorations = new List <string>();
                while (ReadNextToken(xamlReader))
                {
                    if (xamlReader.NodeType == XmlNodeType.Element)
                    {
                        if (!xamlReader.IsEmptyElement)
                        {
                            level++;
                        }
                        if (xamlReader.Name == "TextDecoration")
                        {
                            if (xamlReader.HasAttributes && xamlReader.MoveToAttribute("Location"))
                            {
                                if (xamlReader.Value == "Strikethrough")
                                {
                                    decorations.Add("line-through");
                                }
                                else if (xamlReader.Value == "Underline")
                                {
                                    decorations.Add("underline");
                                }
                            }
                        }
                    }
                    else if (xamlReader.NodeType == XmlNodeType.EndElement)
                    {
                        level--;
                    }
                    if (level <= 0)
                    {
                        if (decorations.Any())
                        {
                            inlineStyle?.Append($"text-decoration:{string.Join(" ", decorations)};");
                        }
                        break;
                    }
                }
                return(false);
            }
            else
            {
                // Skip the element representing the unhandled complex property
                WriteElementContent(xamlReader, /*htmlWriter:*/ null, /*inlineStyle:*/ null, context);
                return(true);
            }
        }
コード例 #6
0
        private static void WriteElementInnerContent(XmlReader xamlReader, XmlWriter htmlWriter, StringBuilder inlineStyle, HtmlFromXamlContext context, ref bool elementContentStarted)
        {
            while (ReadNextToken(xamlReader) && xamlReader.NodeType != XmlNodeType.EndElement)
            {
                switch (xamlReader.NodeType)
                {
                case XmlNodeType.Element:
                    if (!HandleComplexProperty(xamlReader, htmlWriter, inlineStyle, context))
                    {
                        if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
                        {
                            // Output STYLE attribute and clear inlineStyle buffer.
                            htmlWriter.WriteAttributeString("style", inlineStyle.ToString());
                            inlineStyle.Clear();
                        }
                        elementContentStarted = true;
                        if (xamlReader.NodeType == XmlNodeType.Element)
                        {
                            WriteElement(xamlReader, htmlWriter, inlineStyle, context);
                        }
                        else
                        {
                            WriteElementInnerContent(xamlReader, htmlWriter, inlineStyle, context, ref elementContentStarted);
                            return;
                        }
                    }
                    Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement || xamlReader.NodeType == XmlNodeType.Element && xamlReader.IsEmptyElement);
                    break;

                case XmlNodeType.Comment:
                    if (htmlWriter != null)
                    {
                        if (!elementContentStarted && inlineStyle.Length > 0)
                        {
                            htmlWriter.WriteAttributeString("style", inlineStyle.ToString());
                        }
                        htmlWriter.WriteComment(xamlReader.Value);
                    }
                    elementContentStarted = true;
                    break;

                case XmlNodeType.CDATA:
                case XmlNodeType.Text:
                case XmlNodeType.SignificantWhitespace:
                    if (htmlWriter != null)
                    {
                        if (!elementContentStarted && inlineStyle.Length > 0)
                        {
                            htmlWriter.WriteAttributeString("style", inlineStyle.ToString());
                        }
                        var text = xamlReader.Value;
                        context.OnWriteText?.Invoke(xamlReader, htmlWriter, inlineStyle, context, ref text);
                        htmlWriter.WriteString(text);
                    }
                    elementContentStarted = true;
                    break;
                }
            }
        }
コード例 #7
0
        /// <summary>
        /// Reads a content of current xaml element, converts it
        /// </summary>
        /// <param name="xamlReader">
        /// XTextReader which is expected to be at XmlNodeType.Element
        /// (opening element tag) position.
        /// </param>
        /// <param name="htmlWriter">
        /// May be null, in which case we are skipping the xaml element;
        /// witout producing any output to html.
        /// </param>
        /// <param name="inlineStyle">
        /// StringBuilder used for collecting css properties for inline STYLE attribute.
        /// </param>
        /// <param name="context">Conversion context</param>
        private static void WriteElementContent(XmlReader xamlReader, XmlWriter htmlWriter, StringBuilder inlineStyle, HtmlFromXamlContext context)
        {
            Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);

            bool elementContentStarted = false;

            if (xamlReader.IsEmptyElement)
            {
                if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
                {
                    // Output STYLE attribute (if still on element) and clear inlineStyle buffer
                    if (htmlWriter.WriteState == WriteState.Element)
                    {
                        htmlWriter.WriteAttributeString("style", inlineStyle.ToString());
                    }
                    inlineStyle.Clear();
                }
                elementContentStarted = true;
            }
            else
            {
                WriteElementInnerContent(xamlReader, htmlWriter, inlineStyle, context, ref elementContentStarted);

                Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement);
            }
        }
コード例 #8
0
        /// <summary>
        /// Reads attributes of the current xaml element and converts
        /// them into appropriate html attributes or css styles.
        /// </summary>
        /// <param name="xamlReader">
        /// XTextReader which is expected to be at XmlNodeType.Element
        /// (opening element tag) position.
        /// The reader will remain at the same level after function complete.
        /// </param>
        /// <param name="htmlWriter">
        /// TextWriter for output html, which is expected to be in
        /// after WriteStartElement state.
        /// </param>
        /// <param name="inlineStyle">
        /// String builder for collecting css properties for inline STYLE attribute.
        /// </param>
        /// <param name="subElements"></param>
        /// <param name="context">Conversion context</param>
        private static void WriteFormattingProperties(XmlReader xamlReader, XmlWriter htmlWriter, StringBuilder inlineStyle, IList <string> subElements, HtmlFromXamlContext context)
        {
            Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);

            var elementName = xamlReader.LocalName;

            // Clear string builder for the inline style
            inlineStyle.Clear();

            bool borderSet      = false;
            bool borderColorSet = false;

            string fontSizeStyle  = null;
            var    fontSizeIgnore = false;

            if (xamlReader.HasAttributes)
            {
                while (xamlReader.MoveToNextAttribute())
                {
                    string css = null;

                    switch (xamlReader.Name)
                    {
                    // Character fomatting properties
                    // ------------------------------
                    case "Background":
                        css = "background-color:" + ParseXamlColor(xamlReader.Value) + ";";
                        break;

                    case "FontFamily":
                        css = "font-family:" + xamlReader.Value + ";";
                        break;

                    case "FontStyle":
                        css = "font-style:" + xamlReader.Value.ToLower() + ";";
                        break;

                    case "FontWeight":
                        css = "font-weight:" + xamlReader.Value.ToLower() + ";";
                        break;

                    case "FontStretch":
                        break;

                    case "FontSize":
                        double size;
                        if (double.TryParse(xamlReader.Value, out size))
                        {
                            fontSizeStyle = "font-size:" + Math.Round(size, 1, MidpointRounding.AwayFromZero).ToString(CultureInfo.InvariantCulture) + "px;";
                        }
                        else
                        {
                            fontSizeStyle = "font-size:" + xamlReader.Value + "px;";
                        }
                        break;

                    case "Foreground":
                        css = "color:" + ParseXamlColor(xamlReader.Value) + ";";
                        break;

                    case "TextDecorations":
                        css = "text-decoration:underline;";
                        break;

                    case "TextEffects":
                        break;

                    case "Emphasis":
                        break;

                    case "StandardLigatures":
                        break;

                    case "Variants":
                        break;

                    case "Capitals":
                        break;

                    case "Fraction":
                        break;

                    case "BaselineAlignment":
                        if (xamlReader.Value == "Subscript")
                        {
                            subElements.Add("sub");
                            fontSizeIgnore = true;
                        }
                        else if (xamlReader.Value == "Superscript")
                        {
                            subElements.Add("sup");
                            fontSizeIgnore = true;
                        }
                        break;

                    // Paragraph formatting properties
                    // -------------------------------
                    case "Padding":
                        css = "padding:" + ParseXamlThickness(xamlReader.Value) + ";";
                        break;

                    case "Margin":
                        css = "margin:" + ParseXamlThickness(xamlReader.Value) + ";";
                        break;

                    case "BorderThickness":
                        var t  = GetThickness(xamlReader.Value);
                        var bw = PrintThickness(t);
                        css       = $"border-width:{bw};";
                        borderSet = true;
                        break;

                    case "BorderBrush":
                        css            = "border-color:" + ParseXamlColor(xamlReader.Value) + ";";
                        borderColorSet = true;
                        break;

                    case "LineHeight":
                        break;

                    case "TextIndent":
                        css = "text-indent:" + xamlReader.Value + ";";
                        break;

                    case "TextAlignment":
                        css = "text-align:" + xamlReader.Value + ";";
                        break;

                    case "IsKeptTogether":
                        break;

                    case "IsKeptWithNext":
                        break;

                    case "ColumnBreakBefore":
                        break;

                    case "PageBreakBefore":
                        break;

                    case "FlowDirection":
                        break;

                    // Table attributes
                    // ----------------
                    case "Width":
                        css = "width:" + ParseXamlSize(xamlReader.Value) + ";";
                        break;

                    case "ColumnSpan":
                        htmlWriter.WriteAttributeString("COLSPAN", xamlReader.Value);
                        break;

                    case "RowSpan":
                        htmlWriter.WriteAttributeString("ROWSPAN", xamlReader.Value);
                        break;

                    case "CellSpacing":
                        if (xamlReader.Value == "0")
                        {
                            css = "border-collapse:collapse;";
                        }
                        break;

                    default:
                        context.OnWriteCustomProperty?.Invoke(xamlReader, htmlWriter, inlineStyle, context, xamlReader.Name);
                        break;
                    }

                    if (context.OnWriteElementAttribute != null)
                    {
                        css = context.OnWriteElementAttribute(elementName, xamlReader.Name, xamlReader.Value, css);
                    }

                    if (css != null)
                    {
                        inlineStyle.Append(css);
                    }
                }
                if (!fontSizeIgnore && fontSizeStyle != null)
                {
                    inlineStyle.Append(fontSizeStyle);
                }
            }

            if (elementName == "Table")
            {
                if (!borderSet && context.CurrentTable != null)
                {
                    var t         = context.CurrentTable.CommonBorder;
                    var thickness = PrintThickness(new HtmlThickness(0, t.Top, t.Right, 0));
                    inlineStyle.Append($"border-width:{thickness};");
                    borderSet = true;
                }
            }
            if (borderSet || borderColorSet)
            {
                inlineStyle.Append("border-style:solid;");
            }

            context.OnWriteElementStyle?.Invoke(xamlReader, htmlWriter, inlineStyle, context, elementName);

            // Return the xamlReader back to element level
            xamlReader.MoveToElement();
            Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
        }
コード例 #9
0
        /// <summary>
        /// Processes a root level element of XAML (normally it's FlowDocument element).
        /// </summary>
        /// <param name="xamlReader">
        /// XTextReader for a source xaml.
        /// </param>
        /// <param name="htmlWriter">
        /// TextWriter producing resulting html
        /// </param>
        /// <param name="context">Conversion context</param>
        private static bool WriteFlowDocument(XmlReader xamlReader, XmlWriter htmlWriter, HtmlFromXamlContext context)
        {
            if (!ReadNextToken(xamlReader))
            {
                // Xaml content is empty - nothing to convert
                return(false);
            }

            if (xamlReader.NodeType != XmlNodeType.Element || (xamlReader.Name != "FlowDocument" && xamlReader.Name != "Section"))
            {
                // Root FlowDocument elemet is missing
                return(false);
            }

            // Create a buffer StringBuilder for collecting css properties for inline STYLE attributes
            // on every element level (it will be re-initialized on every level).
            var inlineStyle = new StringBuilder();

            if (context.Options.OuterElement != string.Empty)
            {
                htmlWriter.WriteStartElement(context.Options.OuterElement);
            }

            WriteElementWithContent(xamlReader, htmlWriter, context.Options.InnerElement, inlineStyle, context);

            if (context.Options.OuterElement != string.Empty)
            {
                htmlWriter.WriteEndElement();
            }

            return(true);
        }