Click here to Skip to main content
15,885,309 members
Articles / Desktop Programming / Windows Forms

A Professional HTML Renderer You Will Use

Rate me:
Please Sign up or sign in to vote.
4.91/5 (205 votes)
29 Jan 2009BSD4 min read 736.6K   23.3K   531  
100% managed code that draws HTML on any device
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace System.Drawing.Html
{
    public class HtmlTag
    {
        #region Fields

        private string _tagName;
        private bool _isClosing;
        private Dictionary<string, string> _attributes;

        #endregion

        #region Ctor

        private HtmlTag()
        {
            _attributes = new Dictionary<string, string>();
        }

        public HtmlTag(string tag)
            : this()
        {
            tag = tag.Substring(1, tag.Length - 2);

            int spaceIndex = tag.IndexOf(" ");

            //Extract tag name
            if (spaceIndex < 0)
            {
                _tagName = tag;
            }
            else
            {
                _tagName = tag.Substring(0, spaceIndex);
            }

            //Check if is end tag
            if (_tagName.StartsWith("/"))
            {
                _isClosing = true;
                _tagName = _tagName.Substring(1);
            }

            _tagName = _tagName.ToLower();

            //Extract attributes
            MatchCollection atts = Parser.Match(Parser.HmlTagAttributes, tag);

            foreach (Match att in atts)
            {
                //Extract attribute and value
                string[] chunks = att.Value.Split('=');

                if (chunks.Length == 1)
                {
                    if(!Attributes.ContainsKey(chunks[0]))
                        Attributes.Add(chunks[0].ToLower(), string.Empty);
                }
                else if (chunks.Length == 2)
                {
                    string attname = chunks[0].Trim();
                    string attvalue = chunks[1].Trim();

                    if (attvalue.StartsWith("\"") && attvalue.EndsWith("\"") && attvalue.Length > 2)
                    {
                        attvalue = attvalue.Substring(1, attvalue.Length - 2);
                    }

                    if (!Attributes.ContainsKey(attname))
                        Attributes.Add(attname, attvalue);
                }
            }
        }

        #endregion

        #region Props

        /// <summary>
        /// Gets the dictionary of attributes in the tag
        /// </summary>
        public Dictionary<string, string> Attributes
        {
            get { return _attributes; }
        }


        /// <summary>
        /// Gets the name of this tag
        /// </summary>
        public string TagName
        {
            get { return _tagName; }
        }

        /// <summary>
        /// Gets if the tag is actually a closing tag
        /// </summary>
        public bool IsClosing
        {
            get { return _isClosing; }
        }

        /// <summary>
        /// Gets if the tag is single placed; in other words it doesn't need a closing tag; 
        /// e.g. &lt;br&gt;
        /// </summary>
        public bool IsSingle
        {
            get
            {
                return TagName.StartsWith("!")
                    || (new List<string>(
                            new string[]{
                             "area", "base", "basefont", "br", "col",
                             "frame", "hr", "img", "input", "isindex",
                             "link", "meta", "param"
                            }
                        )).Contains(TagName)
                    ;
            }
        }

        internal void TranslateAttributes(CssBox box)
        {
            string t = TagName.ToUpper();

            foreach (string att in Attributes.Keys)
            {
                string value = Attributes[att];

                switch (att)
                {
                    case HtmlConstants.align:
                        if (value == HtmlConstants.left || value == HtmlConstants.center || value == HtmlConstants.right || value == HtmlConstants.justify)
                            box.TextAlign = value;
                        else
                            box.VerticalAlign = value;
                        break;
                    case HtmlConstants.background:
                            box.BackgroundImage = value;
                        break;
                    case HtmlConstants.bgcolor:
                        box.BackgroundColor = value;
                        break;
                    case HtmlConstants.border:
                        box.BorderWidth = TranslateLength(value);
                        
                        if (t == HtmlConstants.TABLE)
                        {
                            ApplyTableBorder(box, value);
                        }
                        else
                        {
                            box.BorderStyle = CssConstants.Solid;
                        }
                        break;
                    case HtmlConstants.bordercolor:
                        box.BorderColor = value;
                        break;
                    case HtmlConstants.cellspacing:
                        box.BorderSpacing = TranslateLength(value);
                        break;
                    case HtmlConstants.cellpadding:
                        ApplyTablePadding(box, value);
                        break;
                    case HtmlConstants.color:
                        box.Color = value;
                        break;
                    case HtmlConstants.dir:
                        box.Direction = value;
                        break;
                    case HtmlConstants.face:
                        box.FontFamily = value;
                        break;
                    case HtmlConstants.height:
                        box.Height = TranslateLength(value);
                        break;
                    case HtmlConstants.hspace:
                        box.MarginRight = box.MarginLeft = TranslateLength(value);
                        break;
                    case HtmlConstants.nowrap:
                        box.WhiteSpace = CssConstants.Nowrap;
                        break;
                    case HtmlConstants.size:
                        if (t == HtmlConstants.HR)
                            box.Height = TranslateLength(value);
                        break;
                    case HtmlConstants.valign:
                        box.VerticalAlign = value;
                        break;
                    case HtmlConstants.vspace:
                        box.MarginTop = box.MarginBottom = TranslateLength(value);
                        break;
                    case HtmlConstants.width:
                        box.Width = TranslateLength(value);
                        break;

                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Converts an HTML length into a Css length
        /// </summary>
        /// <param name="htmlLength"></param>
        /// <returns></returns>
        private string TranslateLength(string htmlLength)
        {
            CssLength len = new CssLength(htmlLength);

            if (len.HasError)
            {
                return htmlLength + "px";
            }

            return htmlLength;
        }

        /// <summary>
        /// Cascades to the TD's the border spacified in the TABLE tag.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="border"></param>
        private void ApplyTableBorder(CssBox table, string border)
        {
            foreach (CssBox box in table.Boxes)
            {
                foreach (CssBox cell in box.Boxes)
                {
                    cell.BorderWidth = TranslateLength(border);
                }
            }
        }

        /// <summary>
        /// Cascades to the TD's the border spacified in the TABLE tag.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="border"></param>
        private void ApplyTablePadding(CssBox table, string padding)
        {
            foreach (CssBox box in table.Boxes)
            {
                foreach (CssBox cell in box.Boxes)
                {
                    cell.Padding = TranslateLength(padding);

                }
            }
        }

        /// <summary>
        /// Gets a boolean indicating if the attribute list has the specified attribute
        /// </summary>
        /// <param name="attribute"></param>
        /// <returns></returns>
        public bool HasAttribute(string attribute)
        {
            return Attributes.ContainsKey(attribute);
        }

        public override string ToString()
        {
            return string.Format("<{1}{0}>", TagName, IsClosing ? "/" : string.Empty);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Product Manager
United States United States
- I've been programming Windows and Web apps since 1997.
- My greatest concern nowadays is product, user interface, and usability.
- TypeScript / React expert

@geeksplainer

Comments and Discussions