コード例 #1
0
		private static CssUsageInfo AnalyzeInternalStyles(Uri pageUrl, HtmlDocument html)
		{
			var inlineCss = html.GetInlineStyles();

			var styleSheet = new CssStylesheet(pageUrl, inlineCss);

			return AnalyzeCssStylesheet(styleSheet, html);
		}
コード例 #2
0
		private static CssUsageInfo AnalyzeCssStylesheet(CssStylesheet stylesheet, HtmlDocument html)
		{
			var unusedSelectors = stylesheet.Selectors
				.AsParallel()
				.Where(s => !html.IsSelectorInUse(s))
				.ToList();
			
			return new CssUsageInfo(stylesheet.Url, unusedSelectors);
		}
コード例 #3
0
    // ---------------------------------------------------------------------
    //
    // Internal Methods
    //
    // ---------------------------------------------------------------------

    #region Internal Methods

    /// <summary>
    /// Converts an html string into xaml string.
    /// </summary>
    /// <param name="htmlString">
    /// Input html which may be badly formated xml.
    /// </param>
    /// <param name="asFlowDocument">
    /// true indicates that we need a FlowDocument as a root element;
    /// false means that Section or Span elements will be used
    /// dependeing on StartFragment/EndFragment comments locations.
    /// </param>
    /// <returns>
    /// Well-formed xml representing XAML equivalent for the input html string.
    /// </returns>
    public static string ConvertHtmlToXaml(string htmlString, bool asFlowDocument)
    {
        // Create well-formed Xml from Html string
        XmlElement htmlElement = HtmlParser.ParseHtml(htmlString);

        // Decide what name to use as a root
        string rootElementName = asFlowDocument ? HtmlToXamlConverter.Xaml_FlowDocument : HtmlToXamlConverter.Xaml_Section;

        // Create an XmlDocument for generated xaml
        XmlDocument xamlTree = new XmlDocument();
        XmlElement xamlFlowDocumentElement = xamlTree.CreateElement(null, rootElementName, _xamlNamespace);

        // Extract style definitions from all STYLE elements in the document
        CssStylesheet stylesheet = new CssStylesheet(htmlElement);

        // Source context is a stack of all elements - ancestors of a parentElement
        List<XmlElement> sourceContext = new List<XmlElement>(10);

        // Clear fragment parent
        InlineFragmentParentElement = null;

        // convert root html element
        AddBlock(xamlFlowDocumentElement, htmlElement, new Hashtable(), stylesheet, sourceContext);

        // In case if the selected fragment is inline, extract it into a separate Span wrapper
        if (!asFlowDocument)
        {
            xamlFlowDocumentElement = ExtractInlineFragment(xamlFlowDocumentElement);
        }

        // Return a string representing resulting Xaml
        xamlFlowDocumentElement.SetAttribute("xml:space", "preserve");
        string xaml = xamlFlowDocumentElement.OuterXml;

        return xaml;
    }
コード例 #4
0
    // .............................................................
    //
    // Text Flow Elements
    //
    // .............................................................

    /// <summary>
    /// Generates Section or Paragraph element from DIV depending whether it contains any block elements or not
    /// </summary>
    /// <param name="xamlParentElement">
    /// XmlElement representing Xaml parent to which the converted element should be added
    /// </param>
    /// <param name="htmlElement">
    /// XmlElement representing Html element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// properties inherited from parent context
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// true indicates that a content added by this call contains at least one block element
    /// </param>
    private static void AddSection(XmlElement xamlParentElement, XmlElement htmlElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Analyze the content of htmlElement to decide what xaml element to choose - Section or Paragraph.
        // If this Div has at least one block child then we need to use Section, otherwise use Paragraph
        bool htmlElementContainsBlocks = false;
        for (XmlNode htmlChildNode = htmlElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
        {
            if (htmlChildNode is XmlElement)
            {
                string htmlChildName = ((XmlElement)htmlChildNode).LocalName.ToLower();
                if (HtmlSchema.IsBlockElement(htmlChildName))
                {
                    htmlElementContainsBlocks = true;
                    break;
                }
            }
        }

        if (!htmlElementContainsBlocks)
        {
            // The Div does not contain any block elements, so we can treat it as a Paragraph
            AddParagraph(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
        }
        else
        {
            // The Div has some nested blocks, so we treat it as a Section

            // Create currentProperties as a compilation of local and inheritedProperties, set localProperties
            Hashtable localProperties;
            Hashtable currentProperties = GetElementProperties(htmlElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

            // Create a XAML element corresponding to this html element
            XmlElement xamlElement = xamlParentElement.OwnerDocument.CreateElement(/*prefix:*/null, /*localName:*/HtmlToXamlConverter.Xaml_Section, _xamlNamespace);
            ApplyLocalProperties(xamlElement, localProperties, /*isBlock:*/true);

            // Decide whether we can unwrap this element as not having any formatting significance.
            if (!xamlElement.HasAttributes)
            {
                // This elements is a group of block elements whitout any additional formatting.
                // We can add blocks directly to xamlParentElement and avoid
                // creating unnecessary Sections nesting.
                xamlElement = xamlParentElement;
            }

            // Recurse into element subtree
            for (XmlNode htmlChildNode = htmlElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode != null ? htmlChildNode.NextSibling : null)
            {
                htmlChildNode = AddBlock(xamlElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
            }

            // Add the new element to the parent.
            if (xamlElement != xamlParentElement)
            {
                xamlParentElement.AppendChild(xamlElement);
            }
        }
    }
コード例 #5
0
    // .............................................................
    //
    // Tables
    //
    // .............................................................

    /// <summary>
    /// Converts htmlTableElement to a Xaml Table element. Adds tbody elements if they are missing so
    /// that a resulting Xaml Table element is properly formed.
    /// </summary>
    /// <param name="xamlParentElement">
    /// Parent xaml element to which a converted table must be added.
    /// </param>
    /// <param name="htmlTableElement">
    /// XmlElement reprsenting the Html table element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// Hashtable representing properties inherited from parent context. 
    /// </param>
    private static void AddTable(XmlElement xamlParentElement, XmlElement htmlTableElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Parameter validation
        Debug.Assert(htmlTableElement.LocalName.ToLower() == "table");
        Debug.Assert(xamlParentElement != null);
        Debug.Assert(inheritedProperties != null);

        // Create current properties to be used by children as inherited properties, set local properties
        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlTableElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        // TODO: process localProperties for tables to override defaults, decide cell spacing defaults

        // Check if the table contains only one cell - we want to take only its content
        XmlElement singleCell = GetCellFromSingleCellTable(htmlTableElement);

        if (singleCell != null)
        {
            //  Need to push skipped table elements onto sourceContext
            sourceContext.Add(singleCell);

            // Add the cell's content directly to parent
            for (XmlNode htmlChildNode = singleCell.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode != null ? htmlChildNode.NextSibling : null)
            {
                htmlChildNode = AddBlock(xamlParentElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
            }

            Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == singleCell);
            sourceContext.RemoveAt(sourceContext.Count - 1);
        }
        else
        {
            // Create xamlTableElement
            XmlElement xamlTableElement = xamlParentElement.OwnerDocument.CreateElement(null, Xaml_Table, _xamlNamespace);

            // Analyze table structure for column widths and rowspan attributes
            ArrayList columnStarts = AnalyzeTableStructure(htmlTableElement, stylesheet);

            // Process COLGROUP & COL elements
            AddColumnInformation(htmlTableElement, xamlTableElement, columnStarts, currentProperties, stylesheet, sourceContext);

            // Process table body - TBODY and TR elements
            XmlNode htmlChildNode = htmlTableElement.FirstChild;

            while (htmlChildNode != null)
            {
                string htmlChildName = htmlChildNode.LocalName.ToLower();

                // Process the element
                if (htmlChildName == "tbody" || htmlChildName == "thead" || htmlChildName == "tfoot")
                {
                    //  Add more special processing for TableHeader and TableFooter
                    XmlElement xamlTableBodyElement = xamlTableElement.OwnerDocument.CreateElement(null, Xaml_TableRowGroup, _xamlNamespace);
                    xamlTableElement.AppendChild(xamlTableBodyElement);

                    sourceContext.Add((XmlElement)htmlChildNode);

                    // Get properties of Html tbody element
                    Hashtable tbodyElementLocalProperties;
                    Hashtable tbodyElementCurrentProperties = GetElementProperties((XmlElement)htmlChildNode, currentProperties, out tbodyElementLocalProperties, stylesheet, sourceContext);
                    // TODO: apply local properties for tbody

                    // Process children of htmlChildNode, which is tbody, for tr elements
                    AddTableRowsToTableBody(xamlTableBodyElement, htmlChildNode.FirstChild, tbodyElementCurrentProperties, columnStarts, stylesheet, sourceContext);
                    if (xamlTableBodyElement.HasChildNodes)
                    {
                        xamlTableElement.AppendChild(xamlTableBodyElement);
                        // else: if there is no TRs in this TBody, we simply ignore it
                    }

                    Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlChildNode);
                    sourceContext.RemoveAt(sourceContext.Count - 1);

                    htmlChildNode = htmlChildNode.NextSibling;
                }
                else if (htmlChildName == "tr")
                {
                    // Tbody is not present, but tr element is present. Tr is wrapped in tbody
                    XmlElement xamlTableBodyElement = xamlTableElement.OwnerDocument.CreateElement(null, Xaml_TableRowGroup, _xamlNamespace);

                    // We use currentProperties of xamlTableElement when adding rows since the tbody element is artificially created and has 
                    // no properties of its own

                    htmlChildNode = AddTableRowsToTableBody(xamlTableBodyElement, htmlChildNode, currentProperties, columnStarts, stylesheet, sourceContext);
                    if (xamlTableBodyElement.HasChildNodes)
                    {
                        xamlTableElement.AppendChild(xamlTableBodyElement);
                    }
                }
                else
                {
                    // Element is not tbody or tr. Ignore it.
                    // TODO: add processing for thead, tfoot elements and recovery for td elements
                    htmlChildNode = htmlChildNode.NextSibling;
                }
            }

            if (xamlTableElement.HasChildNodes)
            {
                xamlParentElement.AppendChild(xamlTableElement);
            }
        }
    }
コード例 #6
0
    /// <summary>
    /// If li items are found without a parent ul/ol element in Html string, creates xamlListElement as their parent and adds
    /// them to it. If the previously added node to the same xamlParentElement was a List, adds the elements to that list.
    /// Otherwise, we create a new xamlListElement and add them to it. Elements are added as long as li elements appear sequentially.
    /// The first non-li or text node stops the addition.
    /// </summary>
    /// <param name="xamlParentElement">
    /// Parent element for the list
    /// </param>
    /// <param name="htmlLIElement">
    /// Start Html li element without parent list
    /// </param>
    /// <param name="inheritedProperties">
    /// Properties inherited from parent context
    /// </param>
    /// <returns>
    /// XmlNode representing the first non-li node in the input after one or more li's have been processed.
    /// </returns>
    private static XmlElement AddOrphanListItems(XmlElement xamlParentElement, XmlElement htmlLIElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        Debug.Assert(htmlLIElement.LocalName.ToLower() == "li");

        XmlElement lastProcessedListItemElement = null;

        // Find out the last element attached to the xamlParentElement, which is the previous sibling of this node
        XmlNode xamlListItemElementPreviousSibling = xamlParentElement.LastChild;
        XmlElement xamlListElement;
        if (xamlListItemElementPreviousSibling != null && xamlListItemElementPreviousSibling.LocalName == Xaml_List)
        {
            // Previously added Xaml element was a list. We will add the new li to it
            xamlListElement = (XmlElement)xamlListItemElementPreviousSibling;
        }
        else
        {
            // No list element near. Create our own.
            xamlListElement = xamlParentElement.OwnerDocument.CreateElement(null, Xaml_List, _xamlNamespace);
            xamlParentElement.AppendChild(xamlListElement);
        }

        XmlNode htmlChildNode = htmlLIElement;
        string htmlChildNodeName = htmlChildNode == null ? null : htmlChildNode.LocalName.ToLower();

        //  Current element properties missed here.
        //currentProperties = GetElementProperties(htmlLIElement, inheritedProperties, out localProperties, stylesheet);

        // Add li elements to the parent xamlListElement we created as long as they appear sequentially
        // Use properties inherited from xamlParentElement for context 
        while (htmlChildNode != null && htmlChildNodeName == "li")
        {
            AddListItem(xamlListElement, (XmlElement)htmlChildNode, inheritedProperties, stylesheet, sourceContext);
            lastProcessedListItemElement = (XmlElement)htmlChildNode;
            htmlChildNode = htmlChildNode.NextSibling;               
            htmlChildNodeName = htmlChildNode == null ? null : htmlChildNode.LocalName.ToLower();
        }

        return lastProcessedListItemElement;
    }
コード例 #7
0
    // .............................................................
    //
    // Images
    //
    // .............................................................

    private static void AddImage(XmlElement xamlParentElement, XmlElement htmlElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        //  Implement images
    }
コード例 #8
0
    private static void AddSpanOrRun(XmlElement xamlParentElement, XmlElement htmlElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Decide what XAML element to use for this inline element.
        // Check whether it contains any nested inlines
        bool elementHasChildren = false;
        for (XmlNode htmlNode = htmlElement.FirstChild; htmlNode != null; htmlNode = htmlNode.NextSibling)
        {
            if (htmlNode is XmlElement)
            {
                string htmlChildName = ((XmlElement)htmlNode).LocalName.ToLower();
                if (HtmlSchema.IsInlineElement(htmlChildName) || HtmlSchema.IsBlockElement(htmlChildName) || 
                    htmlChildName == "img" || htmlChildName == "br" || htmlChildName == "hr")
                {
                    elementHasChildren = true;
                    break;
                }
            }
        }

        string xamlElementName = elementHasChildren ? HtmlToXamlConverter.Xaml_Span : HtmlToXamlConverter.Xaml_Run;

        // Create currentProperties as a compilation of local and inheritedProperties, set localProperties
        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        // Create a XAML element corresponding to this html element
        XmlElement xamlElement = xamlParentElement.OwnerDocument.CreateElement(/*prefix:*/null, /*localName:*/xamlElementName, _xamlNamespace);
        ApplyLocalProperties(xamlElement, localProperties, /*isBlock:*/false);

        // Recurse into element subtree
        for (XmlNode htmlChildNode = htmlElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
        {
            AddInline(xamlElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
        }

        // Add the new element to the parent.
        xamlParentElement.AppendChild(xamlElement);
    }
コード例 #9
0
    /// <summary>
    /// Adds TableCell elements to xamlTableRowElement.
    /// </summary>
    /// <param name="xamlTableRowElement">
    /// XmlElement representing Xaml TableRow element to which the converted cells should be added
    /// </param>
    /// <param name="htmlTDStartNode">
    /// XmlElement representing the child of tr or tbody element from which we should start adding td elements
    /// </param>
    /// <param name="currentProperties">
    /// properties of the current html tr element to which cells are to be added
    /// </param>
    /// <returns>
    /// XmlElement representing the current position of the iterator among the children of the parent Html tbody/tr element
    /// </returns>
    private static XmlNode AddTableCellsToTableRow(XmlElement xamlTableRowElement, XmlNode htmlTDStartNode, Hashtable currentProperties, ArrayList columnStarts, ArrayList activeRowSpans, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // parameter validation
        Debug.Assert(xamlTableRowElement.LocalName == Xaml_TableRow);
        Debug.Assert(currentProperties != null);
        if (columnStarts != null)
        {
            Debug.Assert(activeRowSpans.Count == columnStarts.Count);
        }

        XmlNode htmlChildNode = htmlTDStartNode;
        double columnStart = 0;
        double columnWidth = 0;
        int columnIndex = 0;
        int columnSpan = 0;

        while (htmlChildNode != null && htmlChildNode.LocalName.ToLower() != "tr" && htmlChildNode.LocalName.ToLower() != "tbody" && htmlChildNode.LocalName.ToLower() != "thead" && htmlChildNode.LocalName.ToLower() != "tfoot")
        {
            if (htmlChildNode.LocalName.ToLower() == "td" || htmlChildNode.LocalName.ToLower() == "th")
            {
                XmlElement xamlTableCellElement = xamlTableRowElement.OwnerDocument.CreateElement(null, Xaml_TableCell, _xamlNamespace);

                sourceContext.Add((XmlElement)htmlChildNode);

                Hashtable tdElementLocalProperties;
                Hashtable tdElementCurrentProperties = GetElementProperties((XmlElement)htmlChildNode, currentProperties, out tdElementLocalProperties, stylesheet, sourceContext);

                // TODO: determine if localProperties can be used instead of htmlChildNode in this call, and if they can,
                // make necessary changes and use them instead.
                ApplyPropertiesToTableCellElement((XmlElement)htmlChildNode, xamlTableCellElement);

                if (columnStarts != null)
                {
                    Debug.Assert(columnIndex < columnStarts.Count - 1);
                    while (columnIndex < activeRowSpans.Count && (int)activeRowSpans[columnIndex] > 0)
                    {
                        activeRowSpans[columnIndex] = (int)activeRowSpans[columnIndex] - 1;
                        Debug.Assert((int)activeRowSpans[columnIndex] >= 0);
                        columnIndex++;
                    }
                    Debug.Assert(columnIndex < columnStarts.Count - 1);
                    columnStart = (double)columnStarts[columnIndex];
                    columnWidth = GetColumnWidth((XmlElement)htmlChildNode);
                    columnSpan = CalculateColumnSpan(columnIndex, columnWidth, columnStarts);
                    int rowSpan = GetRowSpan((XmlElement)htmlChildNode);

                    // Column cannot have no span
                    Debug.Assert(columnSpan > 0);
                    Debug.Assert(columnIndex + columnSpan < columnStarts.Count);

                    xamlTableCellElement.SetAttribute(Xaml_TableCell_ColumnSpan, columnSpan.ToString());
                        
                    // Apply row span
                    for (int spannedColumnIndex = columnIndex; spannedColumnIndex < columnIndex + columnSpan; spannedColumnIndex++)
                    {
                        Debug.Assert(spannedColumnIndex < activeRowSpans.Count);
                        activeRowSpans[spannedColumnIndex] = (rowSpan - 1);
                        Debug.Assert((int)activeRowSpans[spannedColumnIndex] >= 0);
                    }

                    columnIndex = columnIndex + columnSpan;
                }

                AddDataToTableCell(xamlTableCellElement, htmlChildNode.FirstChild, tdElementCurrentProperties, stylesheet, sourceContext);
                if (xamlTableCellElement.HasChildNodes)
                {
                    xamlTableRowElement.AppendChild(xamlTableCellElement);
                }

                Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlChildNode);
                sourceContext.RemoveAt(sourceContext.Count - 1);

                htmlChildNode = htmlChildNode.NextSibling;
            }
            else
            {
                // Not td element. Ignore it.
                // TODO: Consider better recovery
                htmlChildNode = htmlChildNode.NextSibling;
            }
        }
        return htmlChildNode;
    }
コード例 #10
0
    /// <summary>
    /// Adds TableRow elements to xamlTableBodyElement. The rows are converted from Html tr elements that
    /// may be the children of an Html tbody element or an Html table element with tbody missing
    /// </summary>
    /// <param name="xamlTableBodyElement">
    /// XmlElement representing Xaml TableRowGroup element to which the converted rows should be added
    /// </param>
    /// <param name="htmlTRStartNode">
    /// XmlElement representing the first tr child of the tbody element to be read
    /// </param>
    /// <param name="currentProperties">
    /// Hashtable representing current properties of the tbody element that are generated and applied in the
    /// AddTable function; to be used as inheritedProperties when adding tr elements
    /// </param>
    /// <param name="columnStarts"></param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// <returns>
    /// XmlNode representing the current position of the iterator among tr elements
    /// </returns>
    private static XmlNode AddTableRowsToTableBody(XmlElement xamlTableBodyElement, XmlNode htmlTRStartNode, Hashtable currentProperties, ArrayList columnStarts, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Parameter validation
        Debug.Assert(xamlTableBodyElement.LocalName == Xaml_TableRowGroup);
        Debug.Assert(currentProperties != null);

        // Initialize child node for iteratimg through children to the first tr element
        XmlNode htmlChildNode = htmlTRStartNode;
        ArrayList activeRowSpans = null;
        if (columnStarts != null)
        {
            activeRowSpans = new ArrayList();
            InitializeActiveRowSpans(activeRowSpans, columnStarts.Count);
        }

        while (htmlChildNode != null && htmlChildNode.LocalName.ToLower() != "tbody")
        {
            if (htmlChildNode.LocalName.ToLower() == "tr")
            {
                XmlElement xamlTableRowElement = xamlTableBodyElement.OwnerDocument.CreateElement(null, Xaml_TableRow, _xamlNamespace);

                sourceContext.Add((XmlElement)htmlChildNode);

                // Get tr element properties
                Hashtable trElementLocalProperties;
                Hashtable trElementCurrentProperties = GetElementProperties((XmlElement)htmlChildNode, currentProperties, out trElementLocalProperties, stylesheet, sourceContext);
                // TODO: apply local properties to tr element

                AddTableCellsToTableRow(xamlTableRowElement, htmlChildNode.FirstChild, trElementCurrentProperties, columnStarts, activeRowSpans, stylesheet, sourceContext);
                if (xamlTableRowElement.HasChildNodes)
                {
                    xamlTableBodyElement.AppendChild(xamlTableRowElement);
                }

                Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlChildNode);
                sourceContext.RemoveAt(sourceContext.Count - 1);

                // Advance
                htmlChildNode = htmlChildNode.NextSibling;
                    
            }
            else if (htmlChildNode.LocalName.ToLower() == "td")
            {
                // Tr element is not present. We create one and add td elements to it
                XmlElement xamlTableRowElement = xamlTableBodyElement.OwnerDocument.CreateElement(null, Xaml_TableRow, _xamlNamespace);
                    
                // This is incorrect formatting and the column starts should not be set in this case
                Debug.Assert(columnStarts == null);

                htmlChildNode = AddTableCellsToTableRow(xamlTableRowElement, htmlChildNode, currentProperties, columnStarts, activeRowSpans, stylesheet, sourceContext);
                if (xamlTableRowElement.HasChildNodes)
                {
                    xamlTableBodyElement.AppendChild(xamlTableRowElement);
                }
            }
            else 
            {
                // Not a tr or td  element. Ignore it.
                // TODO: consider better recovery here
                htmlChildNode = htmlChildNode.NextSibling;
            }
        }
        return htmlChildNode;
    }
コード例 #11
0
    /// <summary>
    /// Converts htmlColElement into Xaml TableColumn element, and appends it to the parent
    /// xamlTableColumnGroupElement
    /// </summary>
    /// <param name="xamlTableElement"></param>
    /// <param name="htmlColElement">
    /// XmlElement representing Html col element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// properties inherited from parent context
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    private static void AddTableColumn(XmlElement xamlTableElement, XmlElement htmlColElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlColElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        XmlElement xamlTableColumnElement = xamlTableElement.OwnerDocument.CreateElement(null, Xaml_TableColumn, _xamlNamespace);

        // TODO: process local properties for TableColumn element

        // Col is an empty element, with no subtree 
        xamlTableElement.AppendChild(xamlTableColumnElement);
    }
コード例 #12
0
    /// <summary>
    /// Converts htmlColgroupElement into Xaml TableColumnGroup element, and appends it to the parent
    /// xamlTableElement
    /// </summary>
    /// <param name="xamlTableElement">
    /// XmlElement representing Xaml Table element to which the converted column group should be added
    /// </param>
    /// <param name="htmlColgroupElement">
    /// XmlElement representing Html colgroup element to be converted
    /// <param name="inheritedProperties">
    /// Properties inherited from parent context
    /// </param>
    private static void AddTableColumnGroup(XmlElement xamlTableElement, XmlElement htmlColgroupElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlColgroupElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        // TODO: process local properties for colgroup

        // Process children of colgroup. Colgroup may contain only col elements.
        for (XmlNode htmlNode = htmlColgroupElement.FirstChild; htmlNode != null; htmlNode = htmlNode.NextSibling)
        {
            if (htmlNode is XmlElement && htmlNode.LocalName.ToLower() == "col")
            {
                AddTableColumn(xamlTableElement, (XmlElement)htmlNode, currentProperties, stylesheet, sourceContext);
            }
        }
    }
コード例 #13
0
    /// <summary>
    /// Processes the information about table columns - COLGROUP and COL html elements.
    /// </summary>
    /// <param name="htmlTableElement">
    /// XmlElement representing a source html table.
    /// </param>
    /// <param name="xamlTableElement">
    /// XmlElement repesenting a resulting xaml table.
    /// </param>
    /// <param name="columnStartsAllRows">
    /// Array of doubles - column start coordinates.
    /// Can be null, which means that column size information is not available
    /// and we must use source colgroup/col information.
    /// In case wneh it's not null, we will ignore source colgroup/col information.
    /// </param>
    /// <param name="currentProperties"></param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    private static void AddColumnInformation(XmlElement htmlTableElement, XmlElement xamlTableElement, ArrayList columnStartsAllRows, Hashtable currentProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Add column information
        if (columnStartsAllRows != null)
        {
            // We have consistent information derived from table cells; use it
            // The last element in columnStarts represents the end of the table
            for (int columnIndex = 0; columnIndex < columnStartsAllRows.Count - 1; columnIndex++)
            {
                XmlElement xamlColumnElement;

                xamlColumnElement = xamlTableElement.OwnerDocument.CreateElement(null, Xaml_TableColumn, _xamlNamespace);
                xamlColumnElement.SetAttribute(Xaml_Width, ((double)columnStartsAllRows[columnIndex + 1] - (double)columnStartsAllRows[columnIndex]).ToString());
                xamlTableElement.AppendChild(xamlColumnElement);
            }
        }
        else
        {
            // We do not have consistent information from table cells;
            // Translate blindly colgroups from html.                
            for (XmlNode htmlChildNode = htmlTableElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
            {
                if (htmlChildNode.LocalName.ToLower() == "colgroup")
                {
                    // TODO: add column width information to this function as a parameter and process it
                    AddTableColumnGroup(xamlTableElement, (XmlElement)htmlChildNode, currentProperties, stylesheet, sourceContext);
                }
                else if (htmlChildNode.LocalName.ToLower() == "col")
                {
                    AddTableColumn(xamlTableElement, (XmlElement)htmlChildNode, currentProperties, stylesheet, sourceContext);
                }
                else if (htmlChildNode is XmlElement)
                {
                    // Some element which belongs to table body. Stop column loop.
                    break;
                }
            }
        }
    }
コード例 #14
0
            // .................................................................
            //
            // Processing CSS Attributes
            //
            // .................................................................

            internal static void GetElementPropertiesFromCssAttributes(XmlElement htmlElement, string elementName, CssStylesheet stylesheet, Hashtable localProperties, List <XmlElement> sourceContext)
            {
                string styleFromStylesheet = stylesheet.GetStyle(elementName, sourceContext);

                string styleInline = HtmlToXamlConverter.GetAttribute(htmlElement, "style");

                // Combine styles from stylesheet and from inline attribute.
                // The order is important - the latter styles will override the former.
                string style = styleFromStylesheet != null ? styleFromStylesheet : null;

                if (styleInline != null)
                {
                    style = style == null ? styleInline : (style + ";" + styleInline);
                }

                // Apply local style to current formatting properties
                if (style != null)
                {
                    string[] styleValues = style.Split(';');
                    for (int i = 0; i < styleValues.Length; i++)
                    {
                        string[] styleNameValue;

                        styleNameValue = styleValues[i].Split(':');
                        if (styleNameValue.Length == 2)
                        {
                            string styleName  = styleNameValue[0].Trim().ToLower();
                            string styleValue = HtmlToXamlConverter.UnQuote(styleNameValue[1].Trim()).ToLower();
                            int    nextIndex  = 0;

                            switch (styleName)
                            {
                            case "font":
                                ParseCssFont(styleValue, localProperties);
                                break;

                            case "font-family":
                                ParseCssFontFamily(styleValue, ref nextIndex, localProperties);
                                break;

                            case "font-size":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, "font-size", /*mustBeNonNegative:*/ true);
                                break;

                            case "font-style":
                                ParseCssFontStyle(styleValue, ref nextIndex, localProperties);
                                break;

                            case "font-weight":
                                ParseCssFontWeight(styleValue, ref nextIndex, localProperties);
                                break;

                            case "font-variant":
                                ParseCssFontVariant(styleValue, ref nextIndex, localProperties);
                                break;

                            case "line-height":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, "line-height", /*mustBeNonNegative:*/ true);
                                break;

                            case "word-spacing":
                                //  Implement word-spacing conversion
                                break;

                            case "letter-spacing":
                                //  Implement letter-spacing conversion
                                break;

                            case "color":
                                ParseCssColor(styleValue, ref nextIndex, localProperties, "color");
                                break;

                            case "text-decoration":
                                ParseCssTextDecoration(styleValue, ref nextIndex, localProperties);
                                break;

                            case "text-transform":
                                ParseCssTextTransform(styleValue, ref nextIndex, localProperties);
                                break;

                            case "background-color":
                                ParseCssColor(styleValue, ref nextIndex, localProperties, "background-color");
                                break;

                            case "background":
                                // TODO: need to parse composite background property
                                ParseCssBackground(styleValue, ref nextIndex, localProperties);
                                break;

                            case "text-align":
                                ParseCssTextAlign(styleValue, ref nextIndex, localProperties);
                                break;

                            case "vertical-align":
                                ParseCssVerticalAlign(styleValue, ref nextIndex, localProperties);
                                break;

                            case "text-indent":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, "text-indent", /*mustBeNonNegative:*/ false);
                                break;

                            case "width":
                            case "height":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/ true);
                                break;

                            case "margin":                                     // top/right/bottom/left
                                ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                                break;

                            case "margin-top":
                            case "margin-right":
                            case "margin-bottom":
                            case "margin-left":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/ true);
                                break;

                            case "padding":
                                ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                                break;

                            case "padding-top":
                            case "padding-right":
                            case "padding-bottom":
                            case "padding-left":
                                ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/ true);
                                break;

                            case "border":
                                ParseCssBorder(styleValue, ref nextIndex, localProperties);
                                break;

                            case "border-style":
                            case "border-width":
                            case "border-color":
                                ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                                break;

                            case "border-top":
                            case "border-right":
                            case "border-left":
                            case "border-bottom":
                                //  Parse css border style
                                break;

                            // NOTE: css names for elementary border styles have side indications in the middle (top/bottom/left/right)
                            // In our internal notation we intentionally put them at the end - to unify processing in ParseCssRectangleProperty method
                            case "border-top-style":
                            case "border-right-style":
                            case "border-left-style":
                            case "border-bottom-style":
                            case "border-top-color":
                            case "border-right-color":
                            case "border-left-color":
                            case "border-bottom-color":
                            case "border-top-width":
                            case "border-right-width":
                            case "border-left-width":
                            case "border-bottom-width":
                                //  Parse css border style
                                break;

                            case "display":
                                //  Implement display style conversion
                                break;

                            case "float":
                                ParseCssFloat(styleValue, ref nextIndex, localProperties);
                                break;

                            case "clear":
                                ParseCssClear(styleValue, ref nextIndex, localProperties);
                                break;

                            default:
                                break;
                            }
                        }
                    }
                }
            }
コード例 #15
0
    /// <summary>
    /// Generates Paragraph element from P, H1-H7, Center etc.
    /// </summary>
    /// <param name="xamlParentElement">
    /// XmlElement representing Xaml parent to which the converted element should be added
    /// </param>
    /// <param name="htmlElement">
    /// XmlElement representing Html element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// properties inherited from parent context
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// true indicates that a content added by this call contains at least one block element
    /// </param>
    private static void AddParagraph(XmlElement xamlParentElement, XmlElement htmlElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Create currentProperties as a compilation of local and inheritedProperties, set localProperties
        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        // Create a XAML element corresponding to this html element
        XmlElement xamlElement = xamlParentElement.OwnerDocument.CreateElement(/*prefix:*/null, /*localName:*/HtmlToXamlConverter.Xaml_Paragraph, _xamlNamespace);
        ApplyLocalProperties(xamlElement, localProperties, /*isBlock:*/true);

        // Recurse into element subtree
        for (XmlNode htmlChildNode = htmlElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
        {
            AddInline(xamlElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
        }

        // Add the new element to the parent.
        xamlParentElement.AppendChild(xamlElement);
    }
コード例 #16
0
    /// <summary>
    /// Creates a Paragraph element and adds all nodes starting from htmlNode
    /// converted to appropriate Inlines.
    /// </summary>
    /// <param name="xamlParentElement">
    /// XmlElement representing Xaml parent to which the converted element should be added
    /// </param>
    /// <param name="htmlNode">
    /// XmlNode starting a collection of implicitly wrapped inlines.
    /// </param>
    /// <param name="inheritedProperties">
    /// properties inherited from parent context
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// true indicates that a content added by this call contains at least one block element
    /// </param>
    /// <returns>
    /// The last htmlNode added to the implicit paragraph
    /// </returns>
    private static XmlNode AddImplicitParagraph(XmlElement xamlParentElement, XmlNode htmlNode, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Collect all non-block elements and wrap them into implicit Paragraph
        XmlElement xamlParagraph = xamlParentElement.OwnerDocument.CreateElement(/*prefix:*/null, /*localName:*/HtmlToXamlConverter.Xaml_Paragraph, _xamlNamespace);
        XmlNode lastNodeProcessed = null;
        while (htmlNode != null)
        {
            if (htmlNode is XmlComment)
            {
                DefineInlineFragmentParent((XmlComment)htmlNode, /*xamlParentElement:*/null);
            }
            else if (htmlNode is XmlText)
            {
                if (htmlNode.Value.Trim().Length > 0)
                {
                    AddTextRun(xamlParagraph, htmlNode.Value);
                }
            }
            else if (htmlNode is XmlElement)
            {
                string htmlChildName = ((XmlElement)htmlNode).LocalName.ToLower();
                if (HtmlSchema.IsBlockElement(htmlChildName))
                {
                    // The sequence of non-blocked inlines ended. Stop implicit loop here.
                    break;
                }
                else
                {
                    AddInline(xamlParagraph, (XmlElement)htmlNode, inheritedProperties, stylesheet, sourceContext);
                }
            }

            // Store last processed node to return it at the end
            lastNodeProcessed = htmlNode;
            htmlNode = htmlNode.NextSibling;
        }

        // Add the Paragraph to the parent
        // If only whitespaces and commens have been encountered,
        // then we have nothing to add in implicit paragraph; forget it.
        if (xamlParagraph.FirstChild != null)
        {
            xamlParentElement.AppendChild(xamlParagraph);
        }

        // Need to return last processed node
        return lastNodeProcessed;
    }
コード例 #17
0
    /// <summary>
    /// adds table cell data to xamlTableCellElement
    /// </summary>
    /// <param name="xamlTableCellElement">
    /// XmlElement representing Xaml TableCell element to which the converted data should be added
    /// </param>
    /// <param name="htmlDataStartNode">
    /// XmlElement representing the start element of data to be added to xamlTableCellElement
    /// </param>
    /// <param name="currentProperties">
    /// Current properties for the html td/th element corresponding to xamlTableCellElement
    /// </param>
    private static void AddDataToTableCell(XmlElement xamlTableCellElement, XmlNode htmlDataStartNode, Hashtable currentProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Parameter validation
        Debug.Assert(xamlTableCellElement.LocalName == Xaml_TableCell);
        Debug.Assert(currentProperties != null);

        for (XmlNode htmlChildNode = htmlDataStartNode; htmlChildNode != null; htmlChildNode = htmlChildNode != null ? htmlChildNode.NextSibling : null)
        {
            // Process a new html element and add it to the td element
            htmlChildNode = AddBlock(xamlTableCellElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
        }
    }
コード例 #18
0
    // .............................................................
    //
    // Inline Elements
    //
    // .............................................................

    private static void AddInline(XmlElement xamlParentElement, XmlNode htmlNode, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        if (htmlNode is XmlComment)
        {
            DefineInlineFragmentParent((XmlComment)htmlNode, xamlParentElement);
        }
        else if (htmlNode is XmlText)
        {
            AddTextRun(xamlParentElement, htmlNode.Value);
        }
        else if (htmlNode is XmlElement)
        {
            XmlElement htmlElement = (XmlElement)htmlNode;

            // Check whether this is an html element
            if (htmlElement.NamespaceURI != HtmlParser.XhtmlNamespace)
            {
                return; // Skip non-html elements
            }

            // Identify element name
            string htmlElementName = htmlElement.LocalName.ToLower();

            // Put source element to the stack
            sourceContext.Add(htmlElement);

            switch (htmlElementName)
            {
                case "a":
                    AddHyperlink(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;
                case "img":
                    AddImage(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;
                case "br":
                case "hr":
                    AddBreak(xamlParentElement, htmlElementName);
                    break;
                default:
                    if (HtmlSchema.IsInlineElement(htmlElementName) || HtmlSchema.IsBlockElement(htmlElementName))
                    {
                        // Note: actually we do not expect block elements here,
                        // but if it happens to be here, we will treat it as a Span.

                        AddSpanOrRun(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    }
                    break;
            }
            // Ignore all other elements non-(block/inline/image)

            // Remove the element from the stack
            Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlElement);
            sourceContext.RemoveAt(sourceContext.Count - 1);
        }
    }
コード例 #19
0
    /// <summary>
    /// Performs a parsing pass over a table to read information about column width and rowspan attributes. This information
    /// is used to determine the starting point of each column. 
    /// </summary>
    /// <param name="htmlTableElement">
    /// XmlElement representing Html table whose structure is to be analyzed
    /// </param>
    /// <returns>
    /// ArrayList of type double which contains the function output. If analysis is successful, this ArrayList contains
    /// all the points which are the starting position of any column in the table, ordered from left to right.
    /// In case if analisys was impossible we return null.
    /// </returns>
    private static ArrayList AnalyzeTableStructure(XmlElement htmlTableElement, CssStylesheet stylesheet)
    {
        // Parameter validation
        Debug.Assert(htmlTableElement.LocalName.ToLower() == "table");
        if (!htmlTableElement.HasChildNodes)
        {
            return null;
        }

        bool columnWidthsAvailable = true;

        ArrayList columnStarts = new ArrayList();
        ArrayList activeRowSpans = new ArrayList();
        Debug.Assert(columnStarts.Count == activeRowSpans.Count);

        XmlNode htmlChildNode = htmlTableElement.FirstChild;
        double tableWidth = 0;  // Keep track of table width which is the width of its widest row

        // Analyze tbody and tr elements
        while (htmlChildNode != null && columnWidthsAvailable)
        {
            Debug.Assert(columnStarts.Count == activeRowSpans.Count);

            switch (htmlChildNode.LocalName.ToLower())
            {
                case "tbody":
                    // Tbody element, we should analyze its children for trows
                    double tbodyWidth = AnalyzeTbodyStructure((XmlElement)htmlChildNode, columnStarts, activeRowSpans, tableWidth, stylesheet);
                    if (tbodyWidth > tableWidth)
                    {
                        // Table width must be increased to supported newly added wide row
                        tableWidth = tbodyWidth;
                    }
                    else if (tbodyWidth == 0)
                    {
                        // Tbody analysis may return 0, probably due to unprocessable format. 
                        // We should also fail.
                        columnWidthsAvailable = false; // interrupt the analisys
                    }
                    break;
                case "tr":
                    // Table row. Analyze column structure within row directly
                    double trWidth = AnalyzeTRStructure((XmlElement)htmlChildNode, columnStarts, activeRowSpans, tableWidth, stylesheet);
                    if (trWidth > tableWidth)
                    {
                        tableWidth = trWidth;
                    }
                    else if (trWidth == 0)
                    {
                        columnWidthsAvailable = false; // interrupt the analisys
                    }
                    break;
                case "td":
                    // Incorrect formatting, too deep to analyze at this level. Return null.
                    // TODO: implement analysis at this level, possibly by creating a new tr
                    columnWidthsAvailable = false; // interrupt the analisys
                    break;
                default:
                    // Element should not occur directly in table. Ignore it.
                    break;
            }

            htmlChildNode = htmlChildNode.NextSibling;
        }

        if (columnWidthsAvailable)
        {
            // Add an item for whole table width
            columnStarts.Add(tableWidth);
            VerifyColumnStartsAscendingOrder(columnStarts);
        }
        else
        {
            columnStarts = null;
        }

        return columnStarts;
    }
コード例 #20
0
    private static void AddHyperlink(XmlElement xamlParentElement, XmlElement htmlElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Convert href attribute into NavigateUri and TargetName
        string href = GetAttribute(htmlElement, "href");
        if (href == null)
        {
            // When href attribute is missing - ignore the hyperlink
            AddSpanOrRun(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
        }
        else
        {
            // Create currentProperties as a compilation of local and inheritedProperties, set localProperties
            Hashtable localProperties;
            Hashtable currentProperties = GetElementProperties(htmlElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

            // Create a XAML element corresponding to this html element
            XmlElement xamlElement = xamlParentElement.OwnerDocument.CreateElement(/*prefix:*/null, /*localName:*/HtmlToXamlConverter.Xaml_Hyperlink, _xamlNamespace);
            ApplyLocalProperties(xamlElement, localProperties, /*isBlock:*/false);

            string[] hrefParts = href.Split(new char[] { '#' });
            if (hrefParts.Length > 0 && hrefParts[0].Trim().Length > 0)
            {
                xamlElement.SetAttribute(HtmlToXamlConverter.Xaml_Hyperlink_NavigateUri, hrefParts[0].Trim());
            }
            if (hrefParts.Length == 2 && hrefParts[1].Trim().Length > 0)
            {
                xamlElement.SetAttribute(HtmlToXamlConverter.Xaml_Hyperlink_TargetName, hrefParts[1].Trim());
            }

            // Recurse into element subtree
            for (XmlNode htmlChildNode = htmlElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
            {
                AddInline(xamlElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
            }

            // Add the new element to the parent.
            xamlParentElement.AppendChild(xamlElement);
        }
    }
コード例 #21
0
    /// <summary>
    /// Performs a parsing pass over a tbody to read information about column width and rowspan attributes. Information read about width
    /// attributes is stored in the reference ArrayList parameter columnStarts, which contains a list of all starting
    /// positions of all columns in the table, ordered from left to right. Row spans are taken into consideration when 
    /// computing column starts
    /// </summary>
    /// <param name="htmlTbodyElement">
    /// XmlElement representing Html tbody whose structure is to be analyzed
    /// </param>
    /// <param name="columnStarts">
    /// ArrayList of type double which contains the function output. If analysis fails, this parameter is set to null
    /// </param>
    /// <param name="tableWidth">
    /// Current width of the table. This is used to determine if a new column when added to the end of table should
    /// come after the last column in the table or is actually splitting the last column in two. If it is only splitting
    /// the last column it should inherit row span for that column
    /// </param>
    /// <returns>
    /// Calculated width of a tbody.
    /// In case of non-analizable column width structure return 0;
    /// </returns>
    private static double AnalyzeTbodyStructure(XmlElement htmlTbodyElement, ArrayList columnStarts, ArrayList activeRowSpans, double tableWidth, CssStylesheet stylesheet)
    {
        // Parameter validation
        Debug.Assert(htmlTbodyElement.LocalName.ToLower() == "tbody");
        Debug.Assert(columnStarts != null);

        double tbodyWidth = 0;
        bool columnWidthsAvailable = true;

        if (!htmlTbodyElement.HasChildNodes)
        {
            return tbodyWidth;
        }

        // Set active row spans to 0 - thus ignoring row spans crossing tbody boundaries
        ClearActiveRowSpans(activeRowSpans);

        XmlNode htmlChildNode = htmlTbodyElement.FirstChild;
          
        // Analyze tr elements
        while (htmlChildNode != null && columnWidthsAvailable)
        {
            switch (htmlChildNode.LocalName.ToLower())
            {
                case "tr":
                    double trWidth = AnalyzeTRStructure((XmlElement)htmlChildNode, columnStarts, activeRowSpans, tbodyWidth, stylesheet);
                    if (trWidth > tbodyWidth)
                    {
                        tbodyWidth = trWidth;
                    }
                    break;
                case "td":
                    columnWidthsAvailable = false; // interrupt the analisys
                    break;
                default:
                    break;
            }
            htmlChildNode = htmlChildNode.NextSibling;
        }

        // Set active row spans to 0 - thus ignoring row spans crossing tbody boundaries
        ClearActiveRowSpans(activeRowSpans);

        return columnWidthsAvailable ? tbodyWidth : 0;
    }
コード例 #22
0
    // .............................................................
    //
    // Lists
    //
    // .............................................................

    /// <summary>
    /// Converts Html ul or ol element into Xaml list element. During conversion if the ul/ol element has any children 
    /// that are not li elements, they are ignored and not added to the list element
    /// </summary>
    /// <param name="xamlParentElement">
    /// XmlElement representing Xaml parent to which the converted element should be added
    /// </param>
    /// <param name="htmlListElement">
    /// XmlElement representing Html ul/ol element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// properties inherited from parent context
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    private static void AddList(XmlElement xamlParentElement, XmlElement htmlListElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        string htmlListElementName = htmlListElement.LocalName.ToLower();

        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlListElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        // Create Xaml List element
        XmlElement xamlListElement = xamlParentElement.OwnerDocument.CreateElement(null, Xaml_List, _xamlNamespace);

        // Set default list markers
        if (htmlListElementName == "ol")
        {
            // Ordered list
            xamlListElement.SetAttribute(HtmlToXamlConverter.Xaml_List_MarkerStyle, Xaml_List_MarkerStyle_Decimal);
        }
        else
        {
            // Unordered list - all elements other than OL treated as unordered lists
            xamlListElement.SetAttribute(HtmlToXamlConverter.Xaml_List_MarkerStyle, Xaml_List_MarkerStyle_Disc);
        }

        // Apply local properties to list to set marker attribute if specified
        // TODO: Should we have separate list attribute processing function?
        ApplyLocalProperties(xamlListElement, localProperties, /*isBlock:*/true);

        // Recurse into list subtree
        for (XmlNode htmlChildNode = htmlListElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode.NextSibling)
        {
            if (htmlChildNode is XmlElement && htmlChildNode.LocalName.ToLower() == "li")
            {
                sourceContext.Add((XmlElement)htmlChildNode);
                AddListItem(xamlListElement, (XmlElement)htmlChildNode, currentProperties, stylesheet, sourceContext);
                Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlChildNode);
                sourceContext.RemoveAt(sourceContext.Count - 1);
            }
            else
            {
                // Not an li element. Add it to previous ListBoxItem
                //  We need to append the content to the end
                // of a previous list item.
            }
        }

        // Add the List element to xaml tree - if it is not empty
        if (xamlListElement.HasChildNodes)
        {
            xamlParentElement.AppendChild(xamlListElement);
        }
    }
コード例 #23
0
    /// <summary>
    /// Performs a parsing pass over a tr element to read information about column width and rowspan attributes.  
    /// </summary>
    /// <param name="htmlTRElement">
    /// XmlElement representing Html tr element whose structure is to be analyzed
    /// </param>
    /// <param name="columnStarts">
    /// ArrayList of type double which contains the function output. If analysis is successful, this ArrayList contains
    /// all the points which are the starting position of any column in the tr, ordered from left to right. If analysis fails,
    /// the ArrayList is set to null
    /// </param>
    /// <param name="activeRowSpans">
    /// ArrayList representing all columns currently spanned by an earlier row span attribute. These columns should
    /// not be used for data in this row. The ArrayList actually contains notation for all columns in the table, if the
    /// active row span is set to 0 that column is not presently spanned but if it is > 0 the column is presently spanned
    /// </param>
    /// <param name="tableWidth">
    /// Double value representing the current width of the table.
    /// Return 0 if analisys was insuccessful.
    /// </param>
    private static double AnalyzeTRStructure(XmlElement htmlTRElement, ArrayList columnStarts, ArrayList activeRowSpans, double tableWidth, CssStylesheet stylesheet)
    {
        double columnWidth;

        // Parameter validation
        Debug.Assert(htmlTRElement.LocalName.ToLower() == "tr");
        Debug.Assert(columnStarts != null);
        Debug.Assert(activeRowSpans != null);
        Debug.Assert(columnStarts.Count == activeRowSpans.Count);

        if (!htmlTRElement.HasChildNodes)
        {
            return 0;
        }

        bool columnWidthsAvailable = true;

        double columnStart = 0; // starting position of current column
        XmlNode htmlChildNode = htmlTRElement.FirstChild;
        int columnIndex = 0;
        double trWidth = 0;

        // Skip spanned columns to get to real column start
        if (columnIndex < activeRowSpans.Count)
        {
            Debug.Assert((double)columnStarts[columnIndex] >= columnStart);
            if ((double)columnStarts[columnIndex] == columnStart)
            {
                // The new column may be in a spanned area
                while (columnIndex < activeRowSpans.Count && (int)activeRowSpans[columnIndex] > 0)
                {
                    activeRowSpans[columnIndex] = (int)activeRowSpans[columnIndex] - 1;
                    Debug.Assert((int)activeRowSpans[columnIndex] >= 0);
                    columnIndex++;
                    columnStart = (double)columnStarts[columnIndex];
                }
            }
        }

        while (htmlChildNode != null && columnWidthsAvailable)
        {
            Debug.Assert(columnStarts.Count == activeRowSpans.Count);

            VerifyColumnStartsAscendingOrder(columnStarts);

            switch (htmlChildNode.LocalName.ToLower())
            {
                case "td":
                    Debug.Assert(columnIndex <= columnStarts.Count);
                    if (columnIndex < columnStarts.Count)
                    {
                        Debug.Assert(columnStart <= (double)columnStarts[columnIndex]);
                        if (columnStart < (double)columnStarts[columnIndex])
                        {
                            columnStarts.Insert(columnIndex, columnStart);
                            // There can be no row spans now - the column data will appear here
                            // Row spans may appear only during the column analysis
                            activeRowSpans.Insert(columnIndex, 0);
                        }
                    }
                    else
                    {
                        // Column start is greater than all previous starts. Row span must still be 0 because
                        // we are either adding after another column of the same row, in which case it should not inherit
                        // the previous column's span. Otherwise we are adding after the last column of some previous
                        // row, and assuming the table widths line up, we should not be spanned by it. If there is
                        // an incorrect tbale structure where a columns starts in the middle of a row span, we do not
                        // guarantee correct output
                        columnStarts.Add(columnStart);
                        activeRowSpans.Add(0);
                    }
                    columnWidth = GetColumnWidth((XmlElement)htmlChildNode);
                    if (columnWidth != -1)
                    {
                        int nextColumnIndex;
                        int rowSpan = GetRowSpan((XmlElement)htmlChildNode);

                        nextColumnIndex = GetNextColumnIndex(columnIndex, columnWidth, columnStarts, activeRowSpans);
                        if (nextColumnIndex != -1)
                        {
                            // Entire column width can be processed without hitting conflicting row span. This means that
                            // column widths line up and we can process them
                            Debug.Assert(nextColumnIndex <= columnStarts.Count);

                            // Apply row span to affected columns
                            for (int spannedColumnIndex = columnIndex; spannedColumnIndex < nextColumnIndex; spannedColumnIndex++)
                            {
                                activeRowSpans[spannedColumnIndex] = rowSpan - 1;
                                Debug.Assert((int)activeRowSpans[spannedColumnIndex] >= 0);
                            }

                            columnIndex = nextColumnIndex;

                            // Calculate columnsStart for the next cell
                            columnStart = columnStart + columnWidth;

                            if (columnIndex < activeRowSpans.Count)
                            {
                                Debug.Assert((double)columnStarts[columnIndex] >= columnStart);
                                if ((double)columnStarts[columnIndex] == columnStart)
                                {
                                    // The new column may be in a spanned area
                                    while (columnIndex < activeRowSpans.Count && (int)activeRowSpans[columnIndex] > 0)
                                    {
                                        activeRowSpans[columnIndex] = (int)activeRowSpans[columnIndex] - 1;
                                        Debug.Assert((int)activeRowSpans[columnIndex] >= 0);
                                        columnIndex++;
                                        columnStart = (double)columnStarts[columnIndex];
                                    }
                                }
                                // else: the new column does not start at the same time as a pre existing column
                                // so we don't have to check it for active row spans, it starts in the middle
                                // of another column which has been checked already by the GetNextColumnIndex function
                            }
                        }
                        else
                        {
                            // Full column width cannot be processed without a pre existing row span.
                            // We cannot analyze widths
                            columnWidthsAvailable = false;
                        }
                    }
                    else
                    {
                        // Incorrect column width, stop processing
                        columnWidthsAvailable = false;
                    }
                    break;
                default:
                    break;
            }

            htmlChildNode = htmlChildNode.NextSibling;
        }

        // The width of the tr element is the position at which it's last td element ends, which is calculated in
        // the columnStart value after each td element is processed
        if (columnWidthsAvailable)
        {
            trWidth = columnStart;
        }
        else
        {
            trWidth = 0;
        }

        return trWidth;
    }
コード例 #24
0
    /// <summary>
    /// Converts htmlLIElement into Xaml ListItem element, and appends it to the parent xamlListElement
    /// </summary>
    /// <param name="xamlListElement">
    /// XmlElement representing Xaml List element to which the converted td/th should be added
    /// </param>
    /// <param name="htmlLIElement">
    /// XmlElement representing Html li element to be converted
    /// </param>
    /// <param name="inheritedProperties">
    /// Properties inherited from parent context
    /// </param>
    private static void AddListItem(XmlElement xamlListElement, XmlElement htmlLIElement, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Parameter validation
        Debug.Assert(xamlListElement != null);
        Debug.Assert(xamlListElement.LocalName == Xaml_List);
        Debug.Assert(htmlLIElement != null);
        Debug.Assert(htmlLIElement.LocalName.ToLower() == "li");
        Debug.Assert(inheritedProperties != null);

        Hashtable localProperties;
        Hashtable currentProperties = GetElementProperties(htmlLIElement, inheritedProperties, out localProperties, stylesheet, sourceContext);

        XmlElement xamlListItemElement = xamlListElement.OwnerDocument.CreateElement(null, Xaml_ListItem, _xamlNamespace);

        // TODO: process local properties for li element

        // Process children of the ListItem
        for (XmlNode htmlChildNode = htmlLIElement.FirstChild; htmlChildNode != null; htmlChildNode = htmlChildNode != null ? htmlChildNode.NextSibling : null)
        {
            htmlChildNode = AddBlock(xamlListItemElement, htmlChildNode, currentProperties, stylesheet, sourceContext);
        }

        // Add resulting ListBoxItem to a xaml parent
        xamlListElement.AppendChild(xamlListItemElement);
    }
コード例 #25
0
    // ---------------------------------------------------------------------
    //
    // Private Methods
    //
    // ---------------------------------------------------------------------

    #region Private Methods

    /// <summary>
    /// Analyzes the given htmlElement expecting it to be converted
    /// into some of xaml Block elements and adds the converted block
    /// to the children collection of xamlParentElement.
    /// 
    /// Analyzes the given XmlElement htmlElement, recognizes it as some HTML element
    /// and adds it as a child to a xamlParentElement.
    /// In some cases several following siblings of the given htmlElement
    /// will be consumed too (e.g. LIs encountered without wrapping UL/OL, 
    /// which must be collected together and wrapped into one implicit List element).
    /// </summary>
    /// <param name="xamlParentElement">
    /// Parent xaml element, to which new converted element will be added
    /// </param>
    /// <param name="htmlElement">
    /// Source html element subject to convert to xaml.
    /// </param>
    /// <param name="inheritedProperties">
    /// Properties inherited from an outer context.
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// <returns>
    /// Last processed html node. Normally it should be the same htmlElement
    /// as was passed as a paramater, but in some irregular cases
    /// it could one of its following siblings.
    /// The caller must use this node to get to next sibling from it.
    /// </returns>
    private static XmlNode AddBlock(XmlElement xamlParentElement, XmlNode htmlNode, Hashtable inheritedProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        if (htmlNode is XmlComment)
        {
            DefineInlineFragmentParent((XmlComment)htmlNode, /*xamlParentElement:*/null);
        }
        else if (htmlNode is XmlText)
        {
            htmlNode = AddImplicitParagraph(xamlParentElement, htmlNode, inheritedProperties, stylesheet, sourceContext);
        }
        else if (htmlNode is XmlElement)
        {
            // Identify element name
            XmlElement htmlElement = (XmlElement)htmlNode;

            string htmlElementName = htmlElement.LocalName; // Keep the name case-sensitive to check xml names
            string htmlElementNamespace = htmlElement.NamespaceURI;

            if (htmlElementNamespace != HtmlParser.XhtmlNamespace)
            {
                // Non-html element. skip it
                // Isn't it too agressive? What if this is just an error in html tag name?
                // TODO: Consider skipping just a wparrer in recursing into the element tree,
                // which may produce some garbage though coming from xml fragments.
                return htmlElement;
            }

            // Put source element to the stack
            sourceContext.Add(htmlElement);

            // Convert the name to lowercase, because html elements are case-insensitive
            htmlElementName = htmlElementName.ToLower();

            // Switch to an appropriate kind of processing depending on html element name
            switch (htmlElementName)
            {
                // Sections:
                case "html":
                case "body":
                case "div":
                case "form": // not a block according to xhtml spec
                case "pre": // Renders text in a fixed-width font
                case "blockquote":
                case "caption":
                case "center":
                case "cite":
                    AddSection(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;

                // Paragraphs:
                case "p":
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                case "nsrtitle":
                case "textarea":
                case "dd": // ???
                case "dl": // ???
                case "dt": // ???
                case "tt": // ???
                    AddParagraph(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;

                case "ol":
                case "ul":
                case "dir": //  treat as UL element
                case "menu": //  treat as UL element
                    // List element conversion
                    AddList(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;
                case "li":
                    // LI outside of OL/UL
                    // Collect all sibling LIs, wrap them into a List and then proceed with the element following the last of LIs
                    htmlNode = AddOrphanListItems(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;

                case "img":
                    // TODO: Add image processing
                    AddImage(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;

                case "table":
                    // hand off to table parsing function which will perform special table syntax checks
                    AddTable(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;

                case "tbody":
                case "tfoot":
                case "thead":
                case "tr":
                case "td":
                case "th":
                    // Table stuff without table wrapper
                    // TODO: add special-case processing here for elements that should be within tables when the
                    // parent element is NOT a table. If the parent element is a table they can be processed normally.
                    // we need to compare against the parent element here, we can't just break on a switch
                    goto default; // Thus we will skip this element as unknown, but still recurse into it.

                case "style": // We already pre-processed all style elements. Ignore it now
                case "meta":
                case "head":
                case "title":
                case "script":
                    // Ignore these elements
                    break;

                default:
                    // Wrap a sequence of inlines into an implicit paragraph
                    htmlNode = AddImplicitParagraph(xamlParentElement, htmlElement, inheritedProperties, stylesheet, sourceContext);
                    break;
            }

            // Remove the element from the stack
            Debug.Assert(sourceContext.Count > 0 && sourceContext[sourceContext.Count - 1] == htmlElement);
            sourceContext.RemoveAt(sourceContext.Count - 1);
        }

        // Return last processed node
        return htmlNode;
    }
コード例 #26
0
    // .................................................................
    //
    // Processing CSS Attributes
    //
    // .................................................................
    internal static void GetElementPropertiesFromCssAttributes(XmlElement htmlElement, string elementName, CssStylesheet stylesheet, Hashtable localProperties, List<XmlElement> sourceContext)
    {
        string styleFromStylesheet = stylesheet.GetStyle(elementName, sourceContext);

        string styleInline = HtmlToXamlConverter.GetAttribute(htmlElement, "style");

        // Combine styles from stylesheet and from inline attribute.
        // The order is important - the latter styles will override the former.
        string style = styleFromStylesheet != null ? styleFromStylesheet : null;
        if (styleInline != null)
        {
            style = style == null ? styleInline : (style + ";" + styleInline);
        }

        // Apply local style to current formatting properties
        if (style != null)
        {
            string[] styleValues = style.Split(';');
            for (int i = 0; i < styleValues.Length; i++)
            {
                string[] styleNameValue;

                styleNameValue = styleValues[i].Split(':');
                if (styleNameValue.Length == 2)
                {
                    string styleName = styleNameValue[0].Trim().ToLower();
                    string styleValue = HtmlToXamlConverter.UnQuote(styleNameValue[1].Trim()).ToLower();
                    int nextIndex = 0;

                    switch (styleName)
                    {
                        case "font":
                            ParseCssFont(styleValue, localProperties);
                            break;
                        case "font-family":
                            ParseCssFontFamily(styleValue, ref nextIndex, localProperties);
                            break;
                        case "font-size":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, "font-size", /*mustBeNonNegative:*/true);
                            break;
                        case "font-style":
                            ParseCssFontStyle(styleValue, ref nextIndex, localProperties);
                            break;
                        case "font-weight":
                            ParseCssFontWeight(styleValue, ref nextIndex, localProperties);
                            break;
                        case "font-variant":
                            ParseCssFontVariant(styleValue, ref nextIndex, localProperties);
                            break;
                        case "line-height":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, "line-height", /*mustBeNonNegative:*/true);
                            break;
                        case "word-spacing":
                            //  Implement word-spacing conversion
                            break;
                        case "letter-spacing":
                            //  Implement letter-spacing conversion
                            break;
                        case "color":
                            ParseCssColor(styleValue, ref nextIndex, localProperties, "color");
                            break;

                        case "text-decoration":
                            ParseCssTextDecoration(styleValue, ref nextIndex, localProperties);
                            break;

                        case "text-transform":
                            ParseCssTextTransform(styleValue, ref nextIndex, localProperties);
                            break;

                        case "background-color":
                            ParseCssColor(styleValue, ref nextIndex, localProperties, "background-color");
                            break;
                        case "background":
                            // TODO: need to parse composite background property
                            ParseCssBackground(styleValue, ref nextIndex, localProperties);
                            break;

                        case "text-align":
                            ParseCssTextAlign(styleValue, ref nextIndex, localProperties);
                            break;
                        case "vertical-align":
                            ParseCssVerticalAlign(styleValue, ref nextIndex, localProperties);
                            break;
                        case "text-indent":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, "text-indent", /*mustBeNonNegative:*/false);
                            break;

                        case "width":
                        case "height":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/true);
                            break;

                        case "margin": // top/right/bottom/left
                            ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                            break;
                        case "margin-top":
                        case "margin-right":
                        case "margin-bottom":
                        case "margin-left":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/true);
                            break;

                        case "padding":
                            ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                            break;
                        case "padding-top":
                        case "padding-right":
                        case "padding-bottom":
                        case "padding-left":
                            ParseCssSize(styleValue, ref nextIndex, localProperties, styleName, /*mustBeNonNegative:*/true);
                            break;

                        case "border":
                            ParseCssBorder(styleValue, ref nextIndex, localProperties);
                            break;
                        case "border-style":
                        case "border-width":
                        case "border-color":
                            ParseCssRectangleProperty(styleValue, ref nextIndex, localProperties, styleName);
                            break;
                        case "border-top":
                        case "border-right":
                        case "border-left":
                        case "border-bottom":
                            //  Parse css border style
                            break;

                        // NOTE: css names for elementary border styles have side indications in the middle (top/bottom/left/right)
                        // In our internal notation we intentionally put them at the end - to unify processing in ParseCssRectangleProperty method
                        case "border-top-style":
                        case "border-right-style":
                        case "border-left-style":
                        case "border-bottom-style":
                        case "border-top-color":
                        case "border-right-color":
                        case "border-left-color":
                        case "border-bottom-color":
                        case "border-top-width":
                        case "border-right-width":
                        case "border-left-width":
                        case "border-bottom-width":
                            //  Parse css border style
                            break;

                        case "display":
                            //  Implement display style conversion
                            break;

                        case "float":
                            ParseCssFloat(styleValue, ref nextIndex, localProperties);
                            break;
                        case "clear":
                            ParseCssClear(styleValue, ref nextIndex, localProperties);
                            break;

                        default:
                            break;
                    }
                }
            }
        }
    }
コード例 #27
0
    /// <summary>
    /// Analyzes the tag of the htmlElement and infers its associated formatted properties.
    /// After that parses style attribute and adds all inline css styles.
    /// The resulting style attributes are collected in output parameter localProperties.
    /// </summary>
    /// <param name="htmlElement">
    /// </param>
    /// <param name="inheritedProperties">
    /// set of properties inherited from ancestor elements. Currently not used in the code. Reserved for the future development.
    /// </param>
    /// <param name="localProperties">
    /// returns all formatting properties defined by this element - implied by its tag, its attributes, or its css inline style
    /// </param>
    /// <param name="stylesheet"></param>
    /// <param name="sourceContext"></param>
    /// <returns>
    /// returns a combination of previous context with local set of properties.
    /// This value is not used in the current code - inntended for the future development.
    /// </returns>
    private static Hashtable GetElementProperties(XmlElement htmlElement, Hashtable inheritedProperties, out Hashtable localProperties, CssStylesheet stylesheet, List<XmlElement> sourceContext)
    {
        // Start with context formatting properties
        Hashtable currentProperties = new Hashtable();
        IDictionaryEnumerator propertyEnumerator = inheritedProperties.GetEnumerator();
        while (propertyEnumerator.MoveNext())
        {
            currentProperties[propertyEnumerator.Key] = propertyEnumerator.Value;
        }

        // Identify element name
        string elementName = htmlElement.LocalName.ToLower();
        string elementNamespace = htmlElement.NamespaceURI;

        // update current formatting properties depending on element tag

        localProperties = new Hashtable();
        switch (elementName)
        {
            // Character formatting
            case "i":
            case "italic":
            case "em":
                localProperties["font-style"] = "italic";
                break;
            case "b":
            case "bold":
            case "strong":
            case "dfn":
                localProperties["font-weight"] = "bold";
                break;
            case "u":
            case "underline":
                localProperties["text-decoration-underline"] = "true";
                break;
            case "font":
                string attributeValue = GetAttribute(htmlElement, "face");
                if (attributeValue != null)
                {
                    localProperties["font-family"] = attributeValue;
                }
                attributeValue = GetAttribute(htmlElement, "size");
                if (attributeValue != null)
                {
                    double fontSize = double.Parse(attributeValue) * (12.0 / 3.0);
                    if (fontSize < 1.0)
                    {
                        fontSize = 1.0;
                    }
                    else if (fontSize > 1000.0)
                    {
                        fontSize = 1000.0;
                    }
                    localProperties["font-size"] = fontSize.ToString();
                }
                attributeValue = GetAttribute(htmlElement, "color");
                if (attributeValue != null)
                {
                    localProperties["color"] = attributeValue;
                }
                break;
            case "samp":
                localProperties["font-family"] = "Courier New"; // code sample
                localProperties["font-size"] = Xaml_FontSize_XXSmall;
                localProperties["text-align"] = "Left";
                break;
            case "sub":
                break;
            case "sup":
                break;

            // Hyperlinks
            case "a": // href, hreflang, urn, methods, rel, rev, title
                //  Set default hyperlink properties
                break;
            case "acronym":
                break;

            // Paragraph formatting:
            case "p":
                //  Set default paragraph properties
                break;
            case "div":
                //  Set default div properties
                break;
            case "pre":
                localProperties["font-family"] = "Courier New"; // renders text in a fixed-width font
                localProperties["font-size"] = Xaml_FontSize_XXSmall;
                localProperties["text-align"] = "Left";
                break;
            case "blockquote":
                localProperties["margin-left"] = "16";
                break;

            case "h1":
                localProperties["font-size"] = Xaml_FontSize_XXLarge;
                break;
            case "h2":
                localProperties["font-size"] = Xaml_FontSize_XLarge;
                break;
            case "h3":
                localProperties["font-size"] = Xaml_FontSize_Large;
                break;
            case "h4":
                localProperties["font-size"] = Xaml_FontSize_Medium;
                break;
            case "h5":
                localProperties["font-size"] = Xaml_FontSize_Small;
                break;
            case "h6":
                localProperties["font-size"] = Xaml_FontSize_XSmall;
                break;
            // List properties
            case "ul":
                localProperties["list-style-type"] = "disc";
                break;
            case "ol":
                localProperties["list-style-type"] = "decimal";
                break;

            case "table":
            case "body":
            case "html":
                break;
        }

        // Override html defaults by css attributes - from stylesheets and inline settings
        HtmlCssParser.GetElementPropertiesFromCssAttributes(htmlElement, elementName, stylesheet, localProperties, sourceContext);

        // Combine local properties with context to create new current properties
        propertyEnumerator = localProperties.GetEnumerator();
        while (propertyEnumerator.MoveNext())
        {
            currentProperties[propertyEnumerator.Key] = propertyEnumerator.Value;
        }

        return currentProperties;
    }