Click here to Skip to main content
15,881,812 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 735.1K   23.4K   531  
100% managed code that draws HTML on any device
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Drawing.Drawing2D;

namespace System.Drawing.Html
{
    /// <summary>
    /// Represents a CSS Box of text or replaced elements.
    /// </summary>
    /// <remarks>
    /// The Box can contains other boxes, that's the way that the CSS Tree
    /// is composed.
    /// 
    /// To know more about boxes visit CSS spec:
    /// http://www.w3.org/TR/CSS21/box.html
    /// </remarks>
    public class CssBox
    {
        #region Static

        /// <summary>
        /// An empty box with empty values.
        /// </summary>
        internal readonly static CssBox Empty;

        /// <summary>
        /// Table of 'css-property' => .NET property
        /// </summary>
        internal static Dictionary<string, PropertyInfo> _properties;

        /// <summary>
        /// Dictionary of default values
        /// </summary>
        private static Dictionary<string, string> _defaults;

        /// <summary>
        /// Hosts all inhertiable properties
        /// </summary>
        private static List<PropertyInfo> _inheritables;

        /// <summary>
        /// Hosts css properties
        /// </summary>
        private static List<PropertyInfo> _cssproperties;

        /// <summary>
        /// Static constructor and initialization
        /// </summary>
        static CssBox()
        {
            #region Initialize _properties, _inheritables and _defaults Dictionaries
            
            _properties = new Dictionary<string, PropertyInfo>();
            _defaults = new Dictionary<string, string>();
            _inheritables = new List<PropertyInfo>();
            _cssproperties = new List<PropertyInfo>();

            PropertyInfo[] props = typeof(CssBox).GetProperties();
            for (int i = 0; i < props.Length; i++)
            {
                CssPropertyAttribute att = Attribute.GetCustomAttribute(props[i], typeof(CssPropertyAttribute)) as CssPropertyAttribute;

                if (att != null)
                {
                    _properties.Add(att.Name, props[i]);
                    _defaults.Add(att.Name, GetDefaultValue(props[i]));
                    _cssproperties.Add(props[i]);

                    CssPropertyInheritedAttribute inh = Attribute.GetCustomAttribute(props[i], typeof(CssPropertyInheritedAttribute)) as CssPropertyInheritedAttribute;

                    if (inh != null)
                    {
                        _inheritables.Add(props[i]);
                    }
                }
            } 
            #endregion

            Empty = new CssBox();
            
        }

        /// <summary>
        /// Gets the default value of the specified css property
        /// </summary>
        /// <param name="prop"></param>
        /// <returns></returns>
        private static string GetDefaultValue(PropertyInfo prop)
        {
            DefaultValueAttribute att = Attribute.GetCustomAttribute(prop, typeof(DefaultValueAttribute)) as DefaultValueAttribute;

            if (att == null) return string.Empty;

            string s = Convert.ToString(att.Value);

            return string.IsNullOrEmpty(s) ? string.Empty : s;
        }

        #endregion

        #region CSS Fields

        private string _backgroundColor;
        private string _backgroundGradient;
        private string _backgroundGradientAngle;
        private string _BackgroundImage;
        private string _backgroundRepeat;
        private string _borderTopWidth;
        private string _borderRightWidth;
        private string _borderBottomWidth;
        private string _borderLeftWidth;
        private string _borderWidth;
        private string _borderTopColor;
        private string _borderRightColor;
        private string _borderBottomColor;
        private string _borderLeftColor;
        private string _borderColor;
        private string _borderTopStyle;
        private string _borderRightStyle;
        private string _borderBottomStyle;
        private string _borderLeftStyle;
        private string _borderStyle;
        private string _borderBottom;
        private string _borderLeft;
        private string _borderRight;
        private string _borderTop;
        private string _borderSpacing;
        private string _borderCollapse;
        private string _border;
        private string _bottom;
        private string _color;
        private string _cornerNWRadius;
        private string _cornerNERadius;
        private string _cornerSERadius;
        private string _cornerSWRadius;
        private string _cornerRadius;
        private string _emptyCells;
        private string _direction;
        private string _display;
        private string _font;
        private string _fontFamily;
        private string _fontSize;
        private string _fontStyle;
        private string _fontVariant;
        private string _fontWeight;
        private string _float;
        private string _height;
        private string _marginBottom;
        private string _marginLeft;
        private string _marginRight;
        private string _marginTop;
        private string _margin;
        private string _left;
        private string _lineHeight;
        private string _listStyleType;
        private string _listStyleImage;
        private string _listStylePosition;
        private string _listStyle;
        private string _paddingLeft;
        private string _paddingBottom;
        private string _paddingRight;
        private string _paddingTop;
        private string _padding;
        private string _right;
        private string _text;
        private string _textAlign;
        private string _textDecoration;
        private string _textIndent;
        private string _top;
        private string _position;
        private string _verticalAlign;
        private string _width;
        private string _wordSpacing;
        private string _whiteSpace;

        #endregion

        #region Fields

        /// <summary>
        /// Do not use or alter this flag
        /// </summary>
        /// <remarks>
        /// Flag that indicates that CssTable algorithm already made fixes on it.
        /// </remarks>
        internal bool TableFixed;

        private List<CssBoxWord> _boxWords;
        private List<CssBox> _boxes;
        private CssBox _parentBox;
        private bool _wordsSizeMeasured;
        private SizeF _size;
        private PointF _location;
        private List<CssLineBox> _lineBoxes;
        private List<CssLineBox> _parentLineBoxes;
        private float _fontAscent = float.NaN;
        private float _fontDescent = float.NaN;
        private float _fontLineSpacing = float.NaN;
        private HtmlTag _htmltag;
        private Dictionary<CssLineBox, RectangleF> _rectangles = null;
        protected InitialContainer _initialContainer = null;
        private CssBox _listItemBox;
        private CssLineBox _firstHostingLineBox;
        private CssLineBox _lastHostingLineBox;

        #endregion

        #region Ctor

        protected CssBox()
        {
            _boxWords = new List<CssBoxWord>();
            _boxes = new List<CssBox>();
            _lineBoxes = new List<CssLineBox>();
            _parentLineBoxes = new List<CssLineBox>();
            _rectangles = new Dictionary<CssLineBox, RectangleF>();

            #region Initialize properties with default values

            foreach (string prop in _properties.Keys)
            {
                _properties[prop].SetValue(this, _defaults[prop], null);
            }

            #endregion
        }

        public CssBox(CssBox parentBox)
            : this()
        {
            ParentBox = parentBox;
        }

        internal CssBox(CssBox parentBox, HtmlTag tag)
            : this(parentBox)
        {
            _htmltag = tag;
        }

        #endregion

        #region CSS Properties

        #region Visual Formatting

        #region Border

        #region Border Width

        [CssProperty("border-bottom-width")]
        [DefaultValue("medium")]
        public string BorderBottomWidth
        {
            get { return _borderBottomWidth; }
            set { _borderBottomWidth = value; }
        }

        [CssProperty("border-left-width")]
        [DefaultValue("medium")]
        public string BorderLeftWidth
        {
            get { return _borderLeftWidth; }
            set { _borderLeftWidth = value; }
        }

        [CssProperty("border-right-width")]
        [DefaultValue("medium")]
        public string BorderRightWidth
        {
            get { return _borderRightWidth; }
            set { _borderRightWidth = value; }
        }

        [CssProperty("border-top-width")]
        [DefaultValue("medium")]
        public string BorderTopWidth
        {
            get { return _borderTopWidth; }
            set { _borderTopWidth = value; }
        }

        [CssProperty("border-width")]
        [DefaultValue("")]
        public string BorderWidth
        {
            get { return _borderWidth; }
            set
            {
                _borderWidth = value;

                string[] values = CssValue.SplitValues(value);

                switch (values.Length)
                {
                    case 1:
                        BorderTopWidth = BorderLeftWidth = BorderRightWidth = BorderBottomWidth = values[0];
                        break;
                    case 2:
                        BorderTopWidth = BorderBottomWidth = values[0];
                        BorderLeftWidth = BorderRightWidth = values[1];
                        break;
                    case 3:
                        BorderTopWidth = values[0];
                        BorderLeftWidth = BorderRightWidth = values[1];
                        BorderBottomWidth = values[2];
                        break;
                    case 4:
                        BorderTopWidth = values[0];
                        BorderRightWidth = values[1];
                        BorderBottomWidth = values[2];
                        BorderLeftWidth = values[3];
                        break;
                    default:
                        break;
                }

            }
        }

        #endregion

        #region Border Style

        [CssProperty("border-bottom-style")]
        [DefaultValue("none")]
        public string BorderBottomStyle
        {
            get { return _borderBottomStyle; }
            set { _borderBottomStyle = value; }
        }


        [CssProperty("border-left-style")]
        [DefaultValue("none")]
        public string BorderLeftStyle
        {
            get { return _borderLeftStyle; }
            set { _borderLeftStyle = value; }
        }

        [CssProperty("border-right-style")]
        [DefaultValue("none")]
        public string BorderRightStyle
        {
            get { return _borderRightStyle; }
            set { _borderRightStyle = value; }
        }

        [CssProperty("border-style")]
        [DefaultValue("")]
        public string BorderStyle
        {
            get { return _borderStyle; }
            set
            {
                _borderStyle = value;

                string[] values = CssValue.SplitValues(value);

                switch (values.Length)
                {
                    case 1:
                        BorderTopStyle = BorderLeftStyle = BorderRightStyle = BorderBottomStyle = values[0];
                        break;
                    case 2:
                        BorderTopStyle = BorderBottomStyle = values[0];
                        BorderLeftStyle = BorderRightStyle = values[1];
                        break;
                    case 3:
                        BorderTopStyle = values[0];
                        BorderLeftStyle = BorderRightStyle = values[1];
                        BorderBottomStyle = values[2];
                        break;
                    case 4:
                        BorderTopStyle = values[0];
                        BorderRightStyle = values[1];
                        BorderBottomStyle = values[2];
                        BorderLeftStyle = values[3];
                        break;
                    default:
                        break;
                }

            }
        }

        [CssProperty("border-top-style")]
        [DefaultValue("none")]
        public string BorderTopStyle
        {
            get { return _borderTopStyle; }
            set { _borderTopStyle = value; }
        }
        #endregion

        #region Border Color

        [CssProperty("border-color")]
        [DefaultValue("black")]
        public string BorderColor
        {
            get { return _borderColor; }
            set
            {
                _borderColor = value;
                
                MatchCollection colors = Parser.Match(Parser.CssColors, value);

                string[] values = new string[colors.Count];

                for (int i = 0; i < values.Length; i++)
                    values[i] = colors[i].Value;

                switch (values.Length)
                {
                    case 1:
                        BorderTopColor = BorderLeftColor = BorderRightColor = BorderBottomColor = values[0];
                        break;
                    case 2:
                        BorderTopColor = BorderBottomColor = values[0];
                        BorderLeftColor = BorderRightColor = values[1];
                        break;
                    case 3:
                        BorderTopColor = values[0];
                        BorderLeftColor = BorderRightColor = values[1];
                        BorderBottomColor = values[2];
                        break;
                    case 4:
                        BorderTopColor = values[0];
                        BorderRightColor = values[1];
                        BorderBottomColor = values[2];
                        BorderLeftColor = values[3];
                        break;
                    default:
                        break;
                }
            }
        }

        [CssProperty("border-bottom-color")]
        [DefaultValue("black")]
        public string BorderBottomColor
        {
            get { return _borderBottomColor; }
            set { _borderBottomColor = value; }
        }

        [CssProperty("border-left-color")]
        [DefaultValue("black")]
        public string BorderLeftColor
        {
            get { return _borderLeftColor; }
            set { _borderLeftColor = value; }
        }

        [CssProperty("border-right-color")]
        [DefaultValue("black")]
        public string BorderRightColor
        {
            get { return _borderRightColor; }
            set { _borderRightColor = value; }
        }

        [CssProperty("border-top-color")]
        [DefaultValue("black")]
        public string BorderTopColor
        {
            get { return _borderTopColor; }
            set { _borderTopColor = value; }
        }

        #endregion

        #region Border ShortHands
        [CssProperty("border")]
        [DefaultValue("")]
        public string Border
        {
            get { return _border; }
            set 
            { 
                _border = value;

                string borderWidth = Parser.Search(Parser.CssBorderWidth, value);
                string borderStyle = Parser.Search(Parser.CssBorderStyle, value);
                string borderColor = Parser.Search(Parser.CssColors, value);

                if (borderWidth != null) BorderWidth = borderWidth;
                if (borderStyle != null) BorderStyle = borderStyle;
                if (borderColor != null) BorderColor = borderColor;
            }
        }

        [CssProperty("border-bottom")]
        [DefaultValue("")]
        public string BorderBottom
        {
            get { return _borderBottom; }
            set
            {
                _borderBottom = value;

                string borderWidth = Parser.Search(Parser.CssBorderWidth, value);
                string borderStyle = Parser.Search(Parser.CssBorderStyle, value);
                string borderColor = Parser.Search(Parser.CssColors, value);

                if (borderWidth != null) BorderBottomWidth = borderWidth;
                if (borderStyle != null) BorderBottomStyle = borderStyle;
                if (borderColor != null) BorderBottomColor = borderColor;
            }
        }

        [CssProperty("border-left")]
        [DefaultValue("")]
        public string BorderLeft
        {
            get { return _borderLeft; }
            set
            {
                _borderLeft = value;

                string borderWidth = Parser.Search(Parser.CssBorderWidth, value);
                string borderStyle = Parser.Search(Parser.CssBorderStyle, value);
                string borderColor = Parser.Search(Parser.CssColors, value);

                if (borderWidth != null) BorderLeftWidth = borderWidth;
                if (borderStyle != null) BorderLeftStyle = borderStyle;
                if (borderColor != null) BorderLeftColor = borderColor;
            }
        }

        [CssProperty("border-right")]
        [DefaultValue("")]
        public string BorderRight
        {
            get { return _borderRight; }
            set
            {
                _borderRight = value;

                string borderWidth = Parser.Search(Parser.CssBorderWidth, value);
                string borderStyle = Parser.Search(Parser.CssBorderStyle, value);
                string borderColor = Parser.Search(Parser.CssColors, value);

                if (borderWidth != null) BorderRightWidth = borderWidth;
                if (borderStyle != null) BorderRightStyle = borderStyle;
                if (borderColor != null) BorderRightColor = borderColor;
            }
        }

        [CssProperty("border-top")]
        [DefaultValue("")]
        public string BorderTop
        {
            get { return _borderTop; }
            set
            {
                _borderTop = value;

                string borderWidth = Parser.Search(Parser.CssBorderWidth, value);
                string borderStyle = Parser.Search(Parser.CssBorderStyle, value);
                string borderColor = Parser.Search(Parser.CssColors, value);

                if (borderWidth != null) BorderTopWidth = borderWidth;
                if (borderStyle != null) BorderTopStyle = borderStyle;
                if (borderColor != null) BorderTopColor = borderColor;
            }
        }

        #endregion

        #region Table borders

        [CssProperty("border-spacing")]
        [DefaultValue("0")]
        [CssPropertyInherited()]
        public string BorderSpacing
        {
            get { return _borderSpacing; }
            set { _borderSpacing = value; }
        }

        [CssProperty("border-collapse")]
        [DefaultValue("separate")]
        [CssPropertyInherited()]
        public string BorderCollapse
        {
            get { return _borderCollapse; }
            set { _borderCollapse = value; }
        }


        #endregion

        #region Rounded corners

        [CssProperty("corner-radius")]
        [DefaultValue("0")]
        public string CornerRadius
        {
            get { return _cornerRadius; }
            set 
            {
                MatchCollection r = Parser.Match(Parser.CssLength, value);

                switch (r.Count)
                {
                    case 1:
                        CornerNERadius = r[0].Value;
                        CornerNWRadius = r[0].Value;
                        CornerSERadius = r[0].Value;
                        CornerSWRadius = r[0].Value;
                        break;
                    case 2:
                        CornerNERadius = r[0].Value;
                        CornerNWRadius = r[0].Value;
                        CornerSERadius = r[1].Value;
                        CornerSWRadius = r[1].Value;
                        break;
                    case 3:
                        CornerNERadius = r[0].Value;
                        CornerNWRadius = r[1].Value;
                        CornerSERadius = r[2].Value;
                        break;
                    case 4:
                        CornerNERadius = r[0].Value;
                        CornerNWRadius = r[1].Value;
                        CornerSERadius = r[2].Value;
                        CornerSWRadius = r[3].Value;
                        break;
                }

                _cornerRadius = value; 
            }
        }


        [CssProperty("corner-nw-radius")]
        [DefaultValue("0")]
        public string CornerNWRadius
        {
            get { return _cornerNWRadius; }
            set { _cornerNWRadius = value; }
        }

        [CssProperty("corner-ne-radius")]
        [DefaultValue("0")]
        public string CornerNERadius
        {
            get { return _cornerNERadius; }
            set { _cornerNERadius = value; }
        }

        [CssProperty("corner-se-radius")]
        [DefaultValue("0")]
        public string CornerSERadius
        {
            get { return _cornerSERadius; }
            set { _cornerSERadius = value; }
        }

        [CssProperty("corner-sw-radius")]
        [DefaultValue("0")]
        public string CornerSWRadius
        {
            get { return _cornerSWRadius; }
            set { _cornerSWRadius = value; }
        }


        #endregion

        #endregion

        #region Margin
        [CssProperty("margin")]
        [DefaultValue("")]
        public string Margin
        {
            get { return _margin; }
            set
            {
                _margin = value;
                string[] values = CssValue.SplitValues(value);

                switch (values.Length)
                {
                    case 1:
                        MarginTop = MarginLeft = MarginRight = MarginBottom = values[0];
                        break;
                    case 2:
                        MarginTop = MarginBottom = values[0];
                        MarginLeft = MarginRight = values[1];
                        break;
                    case 3:
                        MarginTop = values[0];
                        MarginLeft = MarginRight = values[1];
                        MarginBottom = values[2];
                        break;
                    case 4:
                        MarginTop = values[0];
                        MarginRight = values[1];
                        MarginBottom = values[2];
                        MarginLeft = values[3];
                        break;
                    default:
                        break;
                }

            }
        }

        [CssProperty("margin-bottom")]
        [DefaultValue("0")]
        public string MarginBottom
        {
            get { return _marginBottom; }
            set { _marginBottom = value; }
        }

        [CssProperty("margin-left")]
        [DefaultValue("0")]
        public string MarginLeft
        {
            get { return _marginLeft; }
            set { _marginLeft = value; }
        }

        [CssProperty("margin-right")]
        [DefaultValue("0")]
        public string MarginRight
        {
            get { return _marginRight; }
            set { _marginRight = value; }
        }

        [CssProperty("margin-top")]
        [DefaultValue("0")]
        public string MarginTop
        {
            get { return _marginTop; }
            set { _marginTop = value; }
        }

        #endregion

        #region Padding
        [CssProperty("padding")]
        [DefaultValue("")]
        public string Padding
        {
            get { return _padding; }
            set
            {
                _padding = value;

                string[] values = CssValue.SplitValues(value);

                switch (values.Length)
                {
                    case 1:
                        PaddingTop = PaddingLeft = PaddingRight = PaddingBottom = values[0];
                        break;
                    case 2:
                        PaddingTop = PaddingBottom = values[0];
                        PaddingLeft = PaddingRight = values[1];
                        break;
                    case 3:
                        PaddingTop = values[0];
                        PaddingLeft = PaddingRight = values[1];
                        PaddingBottom = values[2];
                        break;
                    case 4:
                        PaddingTop = values[0];
                        PaddingRight = values[1];
                        PaddingBottom = values[2];
                        PaddingLeft = values[3];
                        break;
                    default:
                        break;
                }
            }
        }

        [CssProperty("padding-bottom")]
        [DefaultValue("0")]
        public string PaddingBottom
        {
            get { return _paddingBottom; }
            set { _paddingBottom = value; _actualPaddingBottom = float.NaN; }
        }

        [CssProperty("padding-left")]
        [DefaultValue("0")]
        public string PaddingLeft
        {
            get { return _paddingLeft; }
            set { _paddingLeft = value; _actualPaddingLeft = float.NaN; }
        }

        [CssProperty("padding-right")]
        [DefaultValue("0")]
        public string PaddingRight
        {
            get { return _paddingRight; }
            set { _paddingRight = value; _actualPaddingRight = float.NaN; }
        }

        [CssProperty("padding-top")]
        [DefaultValue("0")]
        public string PaddingTop
        {
            get { return _paddingTop; }
            set { _paddingTop = value; _actualPaddingTop = float.NaN; }
        }
        #endregion 

        #region Bounds

        [CssProperty("left")]
        [DefaultValue("auto")]
        public string Left
        {
            get { return _left; }
            set { _left = value; }
        }

        [CssProperty("top")]
        [DefaultValue("auto")]
        public string Top
        {
            get { return _top; }
            set { _top = value; }
        }

        //[CssProperty("right")]
        //[DefaultValue("auto")]
        //public string Right
        //{
        //    get { return _right; }
        //    set { _right = value; }
        //}

        //[CssProperty("bottom")]
        //[DefaultValue("auto")]
        //public string Bottom
        //{
        //    get { return _bottom; }
        //    set { _bottom = value; }
        //}

        [CssProperty("width")]
        [DefaultValue("auto")]
        public string Width
        {
            get { return _width; }
            set { _width = value; }
        }

        [CssProperty("height")]
        [DefaultValue("auto")]
        public string Height
        {
            get { return _height; }
            set { _height = value; }
        }


        #endregion

        #endregion

        #region Colors and Backgrounds

        [CssProperty("background-color")]
        [DefaultValue("transparent")]
        public string BackgroundColor
        {
            get { return _backgroundColor; }
            set { _backgroundColor = value; }
        }

        [CssProperty("background-image")]
        [DefaultValue("none")]
        public string BackgroundImage
        {
            get { return _BackgroundImage; }
            set { _BackgroundImage = value; }
        }

        [CssProperty("background-repeat")]
        [DefaultValue("repeat")]
        public string BackgroundRepeat
        {
            get { return _backgroundRepeat; }
            set { _backgroundRepeat = value; }
        }

        [CssProperty("background-gradient")]
        [DefaultValue("none")]
        public string BackgroundGradient
        {
            get { return _backgroundGradient; }
            set { _backgroundGradient = value; }
        }

        [CssProperty("background-gradient-angle")]
        [DefaultValue("90")]
        public string BackgroundGradientAngle
        {
            get { return _backgroundGradientAngle; }
            set { _backgroundGradientAngle = value; }
        }

        [CssProperty("color")]
        [DefaultValue("black")]
        [CssPropertyInherited()]
        public string Color
        {
            get { return _color; }
            set { _color = value; _actualColor = System.Drawing.Color.Empty; }
        }

        [CssProperty("display")]
        [DefaultValue("inline")]
        public string Display
        {
            get { return _display; }
            set { _display = value; }
        }

        [CssProperty("direction")]
        [DefaultValue("ltr")]
        public string Direction
        {
            get { return _direction; }
            set { _direction = value; }
        }


        [CssProperty("empty-cells")]
        [DefaultValue("show")]
        [CssPropertyInherited()]
        public string EmptyCells
        {
            get { return _emptyCells; }
            set { _emptyCells = value; }
        }


        [CssProperty("float")]
        [DefaultValue("none")]
        public string Float
        {
            get { return _float; }
            set { _float = value; }
        }

        [CssProperty("position")]
        [DefaultValue("static")]
        public string Position
        {
            get { return _position; }
            set { _position = value; }
        } 
        #endregion

        #region Text


        [CssProperty("line-height")]
        [DefaultValue("normal")]
        public string LineHeight
        {
            get { return _lineHeight; }
            set { _lineHeight = NoEms(value); }
        }

        [CssProperty("vertical-align")]
        [DefaultValue("baseline")]
        [CssPropertyInherited()]
        public string VerticalAlign
        {
            get { return _verticalAlign; }
            set { _verticalAlign = value; }
        }

        [CssProperty("text-indent")]
        [DefaultValue("0")]
        [CssPropertyInherited()]
        public string TextIndent
        {
            get { return _textIndent; }
            set { _textIndent = NoEms(value); }
        }

        [CssProperty("text-align")]
        [DefaultValue("")]
        [CssPropertyInherited()]
        public string TextAlign
        {
            get { return _textAlign; }
            set { _textAlign = value; }
        }

        [CssProperty("text-decoration")]
        [DefaultValue("")]
        public string TextDecoration
        {
            get { return _textDecoration; }
            set { _textDecoration = value; }
        }

        [CssProperty("white-space")]
        [DefaultValue("normal")]
        [CssPropertyInherited()]
        public string WhiteSpace
        {
            get { return _whiteSpace; }
            set { _whiteSpace = value; }
        }


        [CssProperty("word-spacing")]
        [DefaultValue("normal")]
        public string WordSpacing
        {
            get { return _wordSpacing; }
            set { _wordSpacing = NoEms(value); }
        }

        #endregion

        #region Font

        [CssProperty("font")]
        [DefaultValue("")]
        [CssPropertyInherited()]
        public string Font
        {
            get { return _font; }
            set 
            { 
                _font = value;

                int mustBePos;
                string mustBe = Parser.Search(Parser.CssFontSizeAndLineHeight, value, out mustBePos);

                if (!string.IsNullOrEmpty(mustBe))
                {
                    mustBe = mustBe.Trim();
                    //Check for style||variant||weight on the left
                    string leftSide = value.Substring(0, mustBePos);
                    string fontStyle = Parser.Search(Parser.CssFontStyle, leftSide);
                    string fontVariant = Parser.Search(Parser.CssFontVariant, leftSide);
                    string fontWeight = Parser.Search(Parser.CssFontWeight, leftSide);

                    //Check for family on the right
                    string rightSide = value.Substring(mustBePos + mustBe.Length);
                    string fontFamily = rightSide.Trim(); //Parser.Search(Parser.CssFontFamily, rightSide); //TODO: Would this be right?

                    //Check for font-size and line-height
                    string fontSize = mustBe;
                    string lineHeight = string.Empty;

                    if (mustBe.Contains("/") && mustBe.Length > mustBe.IndexOf("/") + 1)
                    {
                        int slashPos = mustBe.IndexOf("/");
                        fontSize = mustBe.Substring(0, slashPos);
                        lineHeight = mustBe.Substring(slashPos + 1);
                    }

                    //Assign values p { font: 12px/14px sans-serif }
                    if (!string.IsNullOrEmpty(fontStyle)) FontStyle = fontStyle;
                    if (!string.IsNullOrEmpty(fontVariant)) FontVariant = fontVariant;
                    if (!string.IsNullOrEmpty(fontWeight)) FontWeight = fontWeight;
                    if (!string.IsNullOrEmpty(fontFamily)) FontFamily = fontFamily;
                    if (!string.IsNullOrEmpty(fontSize)) FontSize = fontSize;
                    if (!string.IsNullOrEmpty(lineHeight)) LineHeight = lineHeight;
                }
                else
                {
                    // Check for: caption | icon | menu | message-box | small-caption | status-bar
                    //TODO: Interpret font values of: caption | icon | menu | message-box | small-caption | status-bar
                }
            }
        }

        [CssProperty("font-family")]
        [DefaultValue("serif")]
        [CssPropertyInherited()]
        public string FontFamily
        {
            get { return _fontFamily; }
            set 
            {

                ///HACK: Because of performance, generic font families
                ///      will be checked when only the generic font 
                ///      family is given.

                switch (value)
                {
                    case CssConstants.Serif:
                        _fontFamily = CssDefaults.FontSerif; break;
                    case CssConstants.SansSerif:
                        _fontFamily = CssDefaults.FontSansSerif; break;
                    case CssConstants.Cursive:
                        _fontFamily = CssDefaults.FontCursive; break;
                    case CssConstants.Fantasy:
                        _fontFamily = CssDefaults.FontFantasy; break;
                    case CssConstants.Monospace:
                        _fontFamily = CssDefaults.FontMonospace; break;
                    default:
                        _fontFamily = value; break;
                }
            }
        }

        [CssProperty("font-size")]
        [DefaultValue("medium")]
        [CssPropertyInherited()]
        public string FontSize
        {
            get { return _fontSize; }
            set 
            {
                string length = Parser.Search(Parser.CssLength, value);

                if (length != null)
                {
                    string computedValue = string.Empty;
                    CssLength len = new CssLength(length);

                    if (len.HasError)
                    {
                        computedValue = _defaults["font-size"];
                    }
                    else if (len.Unit == CssLength.CssUnit.Ems && ParentBox != null)
                    {
                        computedValue = len.ConvertEmToPoints(ParentBox.ActualFont.SizeInPoints).ToString();
                    }
                    else
                    {
                        computedValue = len.ToString();
                    }

                    _fontSize = computedValue;
                }
                else
                {
                    _fontSize = value;
                }

            }
        }

        [CssProperty("font-style")]
        [DefaultValue("normal")]
        [CssPropertyInherited()]
        public string FontStyle
        {
            get { return _fontStyle; }
            set { _fontStyle = value; }
        }

        [CssProperty("font-variant")]
        [DefaultValue("normal")]
        [CssPropertyInherited()]
        public string FontVariant
        {
            get { return _fontVariant; }
            set { _fontVariant = value; }
        }


        [CssProperty("font-weight")]
        [DefaultValue("normal")]
        [CssPropertyInherited()]
        public string FontWeight
        {
            get { return _fontWeight; }
            set { _fontWeight = value; }
        }


        #endregion

        #region Lists

        [CssProperty("list-style")]
        [DefaultValue("")]
        [CssPropertyInherited()]
        public string ListStyle
        {
            get { return _listStyle; }
            set { _listStyle = value; }
        }

        [CssProperty("list-style-position")]
        [DefaultValue("outside")]
        [CssPropertyInherited()]
        public string ListStylePosition
        {
            get { return _listStylePosition; }
            set { _listStylePosition = value; }
        }

        [CssProperty("list-style-image")]
        [DefaultValue("")]
        [CssPropertyInherited()]
        public string ListStyleImage
        {
            get { return _listStyleImage; }
            set { _listStyleImage = value; }
        }

        [CssProperty("list-style-type")]
        [DefaultValue("disc")]
        [CssPropertyInherited()]
        public string ListStyleType
        {
            get { return _listStyleType; }
            set { _listStyleType = value; }
        }


        #endregion

        #endregion

        #region Actual Values Properties

        #region Fields
        private float _actualCornerNW = float.NaN;
        private float _actualCornerNE = float.NaN;
        private float _actualCornerSW = float.NaN;
        private float _actualCornerSE = float.NaN;
        private Color _actualColor = System.Drawing.Color.Empty;
        private float _actualBackgroundGradientAngle = float.NaN;
        private float _actualPaddingTop = float.NaN;
        private float _actualPaddingBottom = float.NaN;
        private float _actualPaddingRight = float.NaN;
        private float _actualPaddingLeft = float.NaN; 
        private float _actualMarginTop = float.NaN;
        private float _actualMarginBottom = float.NaN;
        private float _actualMarginRight = float.NaN;
        private float _actualMarginLeft = float.NaN;
        private float _actualBorderTopWidth = float.NaN;
        private float _actualBorderLeftWidth = float.NaN;
        private float _actualBorderBottomWidth = float.NaN;
        private float _actualBorderRightWidth = float.NaN;
        private Color _actualBackgroundGradient = System.Drawing.Color.Empty;
        private System.Drawing.Color _actualBorderTopColor = System.Drawing.Color.Empty;
        private System.Drawing.Color _actualBorderLeftColor = System.Drawing.Color.Empty;
        private System.Drawing.Color _actualBorderBottomColor = System.Drawing.Color.Empty;
        private System.Drawing.Color _actualBorderRightColor = System.Drawing.Color.Empty;
        private float _actualWordSpacing = float.NaN;
        private Color _actualBackgroundColor = System.Drawing.Color.Empty;
        private Font _actualFont = null;
        private float _actualTextIndent = float.NaN;
        private float _actualBorderSpacingHorizontal = float.NaN;
        private float _actualBorderSpacingVertical = float.NaN;

        #endregion

        #region Boxing
        #region Padding
        /// <summary>
        /// Gets the actual top's padding
        /// </summary>
        public float ActualPaddingTop
        {
            get
            {

                if (float.IsNaN(_actualPaddingTop))
                {
                    _actualPaddingTop = CssValue.ParseLength(PaddingTop, Size.Width, this);
                }

                return _actualPaddingTop;

            }
        }

        /// <summary>
        /// Gets the actual padding on the left
        /// </summary>
        public float ActualPaddingLeft
        {
            get
            {
                if (float.IsNaN(_actualPaddingLeft))
                {
                    _actualPaddingLeft = CssValue.ParseLength(PaddingLeft, Size.Width, this);
                }
                return _actualPaddingLeft;
            }
        }

        /// <summary>
        /// Gets the actual Padding of the bottom
        /// </summary>
        public float ActualPaddingBottom
        {
            get
            {
                if (float.IsNaN(_actualPaddingBottom))
                {
                    _actualPaddingBottom = CssValue.ParseLength(PaddingBottom, Size.Width, this);
                }
                return _actualPaddingBottom;
            }
        }

        /// <summary>
        /// Gets the actual padding on the right
        /// </summary>
        public float ActualPaddingRight
        {
            get
            {
                if (float.IsNaN(_actualPaddingRight))
                {
                    _actualPaddingRight = CssValue.ParseLength(PaddingRight, Size.Width, this);
                }
                return _actualPaddingRight;
            }
        }

        #endregion

        #region Margin
        /// <summary>
        /// Gets the actual top's Margin
        /// </summary>
        public float ActualMarginTop
        {
            get
            {

                if (float.IsNaN(_actualMarginTop))
                {
                    if (MarginTop == CssConstants.Auto) MarginTop = "0";
                    _actualMarginTop = CssValue.ParseLength(MarginTop, Size.Width, this);
                }

                return _actualMarginTop;

            }
        }

        /// <summary>
        /// Gets the actual Margin on the left
        /// </summary>
        public float ActualMarginLeft
        {
            get
            {
                if (float.IsNaN(_actualMarginLeft))
                {
                    if (MarginLeft == CssConstants.Auto) MarginLeft = "0";
                    _actualMarginLeft = CssValue.ParseLength(MarginLeft, Size.Width, this);
                }
                return _actualMarginLeft;
            }
        }

        /// <summary>
        /// Gets the actual Margin of the bottom
        /// </summary>
        public float ActualMarginBottom
        {
            get
            {
                if (float.IsNaN(_actualMarginBottom))
                {
                    if (MarginBottom == CssConstants.Auto) MarginBottom = "0";
                    _actualMarginBottom = CssValue.ParseLength(MarginBottom, Size.Width, this);
                }
                return _actualMarginBottom;
            }
        }

        /// <summary>
        /// Gets the actual Margin on the right
        /// </summary>
        public float ActualMarginRight
        {
            get
            {
                if (float.IsNaN(_actualMarginRight))
                {
                    if (MarginRight == CssConstants.Auto) MarginRight = "0";
                    _actualMarginRight = CssValue.ParseLength(MarginRight, Size.Width, this);
                }
                return _actualMarginRight;
            }
        }
        #endregion

        #region Border

        #region Border Width

        /// <summary>
        /// Gets the actual top border width
        /// </summary>
        public float ActualBorderTopWidth
        {
            get
            {
                if (float.IsNaN(_actualBorderTopWidth))
                {
                    _actualBorderTopWidth = CssValue.GetActualBorderWidth(BorderTopWidth, this);

                    if (string.IsNullOrEmpty(BorderTopStyle) || BorderTopStyle == CssConstants.None)
                    {
                        _actualBorderTopWidth = 0f;
                    }
                }

                return _actualBorderTopWidth;
            }
        }


        /// <summary>
        /// Gets the actual Left border width
        /// </summary>
        public float ActualBorderLeftWidth
        {
            get
            {
                if (float.IsNaN(_actualBorderLeftWidth))
                {
                    _actualBorderLeftWidth = CssValue.GetActualBorderWidth(BorderLeftWidth, this);

                    if (string.IsNullOrEmpty(BorderLeftStyle) || BorderLeftStyle == CssConstants.None)
                    {
                        _actualBorderLeftWidth = 0f;
                    }
                }

                return _actualBorderLeftWidth;
            }
        }


        /// <summary>
        /// Gets the actual Bottom border width
        /// </summary>
        public float ActualBorderBottomWidth
        {
            get
            {
                if (float.IsNaN(_actualBorderBottomWidth))
                {
                    _actualBorderBottomWidth = CssValue.GetActualBorderWidth(BorderBottomWidth, this);

                    if (string.IsNullOrEmpty(BorderBottomStyle) || BorderBottomStyle == CssConstants.None)
                    {
                        _actualBorderBottomWidth = 0f;
                    }
                }

                return _actualBorderBottomWidth;
            }
        }


        /// <summary>
        /// Gets the actual Right border width
        /// </summary>
        public float ActualBorderRightWidth
        {
            get
            {
                if (float.IsNaN(_actualBorderRightWidth))
                {
                    _actualBorderRightWidth = CssValue.GetActualBorderWidth(BorderRightWidth, this);

                    if (string.IsNullOrEmpty(BorderRightStyle) || BorderRightStyle == CssConstants.None)
                    {
                        _actualBorderRightWidth = 0f;
                    }
                }

                return _actualBorderRightWidth;
            }
        }

        #endregion

        #region Border Color

        /// <summary>
        /// Gets the actual top border Color
        /// </summary>
        public Color ActualBorderTopColor
        {
            get
            {
                if ((_actualBorderTopColor.IsEmpty))
                {
                    _actualBorderTopColor = CssValue.GetActualColor(BorderTopColor);
                }

                return _actualBorderTopColor;
            }
        }


        /// <summary>
        /// Gets the actual Left border Color
        /// </summary>
        public Color ActualBorderLeftColor
        {
            get
            {
                if ((_actualBorderLeftColor.IsEmpty))
                {
                    _actualBorderLeftColor = CssValue.GetActualColor(BorderLeftColor);
                }

                return _actualBorderLeftColor;
            }
        }


        /// <summary>
        /// Gets the actual Bottom border Color
        /// </summary>
        public Color ActualBorderBottomColor
        {
            get
            {
                if ((_actualBorderBottomColor.IsEmpty))
                {
                    _actualBorderBottomColor = CssValue.GetActualColor(BorderBottomColor);
                }

                return _actualBorderBottomColor;
            }
        }


        /// <summary>
        /// Gets the actual Right border Color
        /// </summary>
        public Color ActualBorderRightColor
        {
            get
            {
                if ((_actualBorderRightColor.IsEmpty))
                {
                    _actualBorderRightColor = CssValue.GetActualColor(BorderRightColor);
                }

                return _actualBorderRightColor;
            }
        }

        #endregion

        #endregion 

        #region Corners

        /// <summary>
        /// Gets the actual lenght of the north west corner
        /// </summary>
        public float ActualCornerNW
        {
            get 
            {
                if (float.IsNaN(_actualCornerNW))
                {
                    _actualCornerNW = CssValue.ParseLength(CornerNWRadius, 0, this);
                }

                return _actualCornerNW; 
            }
        }

        /// <summary>
        /// Gets the actual lenght of the north east corner
        /// </summary>
        public float ActualCornerNE
        {
            get
            {
                if (float.IsNaN(_actualCornerNE))
                {
                    _actualCornerNE = CssValue.ParseLength(CornerNERadius, 0, this);
                }
                return _actualCornerNE;
            }
        }

        /// <summary>
        /// Gets the actual lenght of the south east corner
        /// </summary>
        public float ActualCornerSE
        {
            get
            {
                if (float.IsNaN(_actualCornerSE))
                {
                    _actualCornerSE = CssValue.ParseLength(CornerSERadius, 0, this);
                }

                return _actualCornerSE;
            }
        }

        /// <summary>
        /// Gets the actual lenght of the south west corner
        /// </summary>
        public float ActualCornerSW
        {
            get
            {
                if (float.IsNaN(_actualCornerSW))
                {
                    _actualCornerSW = CssValue.ParseLength(CornerSWRadius, 0, this);
                }

                return _actualCornerSW;
            }
        }


        #endregion
        #endregion

        #region Layout Formatting

        /// <summary>
        /// Gets the actual word spacing of the word.
        /// </summary>
        public float ActualWordSpacing
        {
            get 
            {
                if (float.IsNaN(_actualWordSpacing))
                {
                    throw new Exception("Space must be calculated before using this property");
                }
                return _actualWordSpacing; 
            }
        }


        #endregion

        #region Colors and Backgrounds

        /// <summary>
        /// 
        /// Gets the actual color for the text.
        /// </summary>
        public Color ActualColor
        {
            get
            {

                if (_actualColor.IsEmpty)
                {
                    _actualColor = CssValue.GetActualColor(Color);
                }

                return _actualColor;

            }
        }

        /// <summary>
        /// Gets the actual background color of the box
        /// </summary>
        public Color ActualBackgroundColor
        {
            get 
            {
                if (_actualBackgroundColor.IsEmpty)
                {
                    _actualBackgroundColor = CssValue.GetActualColor(BackgroundColor);
                }

                return _actualBackgroundColor; 
            }
        }

        /// <summary>
        /// Gets the second color that creates a gradient for the background
        /// </summary>
        public Color ActualBackgroundGradient
        {
            get 
            {
                if (_actualBackgroundGradient.IsEmpty)
                {
                    _actualBackgroundGradient = CssValue.GetActualColor(BackgroundGradient);
                }
                return _actualBackgroundGradient; 
            }
        }


        /// <summary>
        /// Gets the actual angle specified for the background gradient
        /// </summary>
        public float ActualBackgroundGradientAngle
        {
            get 
            {
                if (float.IsNaN(_actualBackgroundGradientAngle))
                {
                    _actualBackgroundGradientAngle = CssValue.ParseNumber(BackgroundGradientAngle, 360f);
                }

                return _actualBackgroundGradientAngle;
            }
        }

        #endregion

        #region Fonts

        /// <summary>
        /// Gets the actual font of the parent
        /// </summary>
        public Font ActualParentFont
        {
            get 
            {
                if (ParentBox == null)
                {
                    return ActualFont;
                }

                return ParentBox.ActualFont;
            }
        }

        /// <summary>
        /// Gets the font that should be actually used to paint the text of the box
        /// </summary>
        public Font ActualFont
        {
            get {
                if (_actualFont == null)
                {
                    if (string.IsNullOrEmpty(FontFamily)) { FontFamily = CssDefaults.FontSerif; }
                    if (string.IsNullOrEmpty(FontSize)) { FontSize = CssDefaults.FontSize + "pt"; }

                    FontStyle st = System.Drawing.FontStyle.Regular;

                    if (FontStyle == CssConstants.Italic || FontStyle == CssConstants.Oblique)
                    {
                        st |= System.Drawing.FontStyle.Italic;
                    }

                    if (FontWeight != CssConstants.Normal && FontWeight != CssConstants.Lighter && !string.IsNullOrEmpty(FontWeight))
                    {
                        st |= System.Drawing.FontStyle.Bold;
                    }

                    float fsize = 0f;
                    float parentSize = CssDefaults.FontSize;

                    if(ParentBox != null) parentSize = ParentBox.ActualFont.Size;

                    switch (FontSize)
                    {
                        case CssConstants.Medium:
                            fsize = CssDefaults.FontSize; break;
                        case CssConstants.XXSmall:
                            fsize = CssDefaults.FontSize - 4; break;
                        case CssConstants.XSmall:
                            fsize = CssDefaults.FontSize - 3; break;
                        case CssConstants.Small:
                            fsize = CssDefaults.FontSize - 2; break;
                        case CssConstants.Large:
                            fsize = CssDefaults.FontSize + 2; break;
                        case CssConstants.XLarge:
                            fsize = CssDefaults.FontSize + 3; break;
                        case CssConstants.XXLarge:
                            fsize = CssDefaults.FontSize + 4; break;
                        case CssConstants.Smaller:
                            fsize = parentSize - 2; break;
                        case CssConstants.Larger:
                            fsize = parentSize + 2; break;
                        default:
                            fsize = CssValue.ParseLength(FontSize, parentSize, this, parentSize, true);
                            break;
                    }

                    if (fsize <= 1f) fsize = CssDefaults.FontSize;

                    _actualFont = new Font(FontFamily, fsize, st);
                }
                return _actualFont; 
            }
        }

        #endregion

        #region Text


        /// <summary>
        /// Gets the text indentation (on first line only)
        /// </summary>
        public float ActualTextIndent
        {
            get
            {
                if (float.IsNaN(_actualTextIndent))
                {
                    _actualTextIndent = CssValue.ParseLength(TextIndent, Size.Width, this);
                }

                return _actualTextIndent; 
            }
        }


        #endregion

        #region Tables

        /// <summary>
        /// Gets the actual horizontal border spacing for tables
        /// </summary>
        public float ActualBorderSpacingHorizontal
        {
            get 
            {
                if (float.IsNaN(_actualBorderSpacingHorizontal))
                {
                    MatchCollection matches = Parser.Match(Parser.CssLength, BorderSpacing);

                    if (matches.Count == 0)
                    {
                        _actualBorderSpacingHorizontal = 0;
                    }
                    else if (matches.Count > 0)
                    {
                        _actualBorderSpacingHorizontal = CssValue.ParseLength(matches[0].Value, 1, this);
                    } 
                }
                

                return _actualBorderSpacingHorizontal; 
            }
        }

        /// <summary>
        /// Gets the actual vertical border spacing for tables
        /// </summary>
        public float ActualBorderSpacingVertical
        {
            get
            {
                if (float.IsNaN(_actualBorderSpacingVertical))
                {
                    MatchCollection matches = Parser.Match(Parser.CssLength, BorderSpacing);

                    if (matches.Count == 0)
                    {
                        _actualBorderSpacingVertical = 0;
                    }
                    else if (matches.Count == 1)
                    {
                        _actualBorderSpacingVertical = CssValue.ParseLength(matches[0].Value, 1, this);
                    }
                    else
                    {
                        _actualBorderSpacingVertical = CssValue.ParseLength(matches[1].Value, 1, this);
                    } 
                }
                return _actualBorderSpacingVertical; 
            }
        }


        #endregion

        #endregion

        #region Properties

        /// <summary>
        /// Gets the � box
        /// </summary>
        public CssBox ListItemBox
        {
            get 
            {
                return _listItemBox; 
            }
        }

        /// <summary>
        /// Gets the width available on the box, counting padding and margin.
        /// </summary>
        public float AvailableWidth
        {
            get { return Size.Width - ActualBorderLeftWidth - ActualPaddingLeft - ActualPaddingRight - ActualBorderRightWidth; }
        }

        /// <summary>
        /// Gets the bounds of the box
        /// </summary>
        public RectangleF Bounds
        {
            get { return new RectangleF(Location, Size); }
        }

        /// <summary>
        /// Gets or sets the bottom of the box. 
        /// (When setting, alters only the Size.Height of the box)
        /// </summary>
        public float ActualBottom
        {
            get 
            {
                return Location.Y + Size.Height;
            }
            set 
            {
                Size = new SizeF(Size.Width, value - Location.Y);
            }
        }

        /// <summary>
        /// Gets the childrenn boxes of this box
        /// </summary>
        public List<CssBox> Boxes
        {
            get { return _boxes; }
        }

        /// <summary>
        /// Gets the left of the client rectangle (Where content starts rendering)
        /// </summary>
        public float ClientLeft
        {
            get { return Location.X + ActualBorderLeftWidth + ActualPaddingLeft; }
        }

        /// <summary>
        /// Gets the top of the client rectangle (Where content starts rendering)
        /// </summary>
        public float ClientTop
        {
            get { return Location.Y + ActualBorderTopWidth + ActualPaddingTop; }
        }

        /// <summary>
        /// Gets the right of the client rectangle
        /// </summary>
        public float ClientRight
        {
            get { return ActualRight - ActualPaddingRight - ActualBorderRightWidth; }
        }

        /// <summary>
        /// Gets the bottom of the client rectangle
        /// </summary>
        public float ClientBottom
        {
            get { return ActualBottom - ActualPaddingBottom - ActualBorderBottomWidth; }
        }

        /// <summary>
        /// Gets the client rectangle
        /// </summary>
        public RectangleF ClientRectangle
        {
            get { return RectangleF.FromLTRB(ClientLeft, ClientTop, ClientRight, ClientBottom); }
        }

        /// <summary>
        /// Gets the containing block-box of this box. (The nearest parent box with display=block)
        /// </summary>
        public CssBox ContainingBlock
        {
            get
            {
                if (ParentBox == null)
                {
                    return this; //This is the initial containing block.
                }

                CssBox b = ParentBox;

                while (
                    b.Display != CssConstants.Block && 
                    b.Display != CssConstants.Table &&
                    b.Display != CssConstants.TableCell &&
                    b.ParentBox != null)
                {
                    b = b.ParentBox;
                }

                //Comment this following line to treat always superior box as block
                if (b == null) throw new Exception("There's no containing block on the chain");

                return b;
            }
        }

        /// <summary>
        /// Gets the font's ascent
        /// </summary>
        public float FontAscent
        {
            get
            {
                if (float.IsNaN(_fontAscent))
                {
                    _fontAscent = CssLayoutEngine.GetAscent(ActualFont);
                }
                return _fontAscent;
            }
        }

        /// <summary>
        /// Gets the font's line spacing
        /// </summary>
        public float FontLineSpacing
        {
            get 
            {
                if (float.IsNaN(_fontLineSpacing))
                {
                    _fontLineSpacing = CssLayoutEngine.GetLineSpacing(ActualFont);
                }

                return _fontLineSpacing;
            }
        }

        /// <summary>
        /// Gets the font's descent
        /// </summary>
        public float FontDescent
        {
            get
            {
                if (float.IsNaN(_fontDescent))
                {
                    _fontDescent = CssLayoutEngine.GetDescent(ActualFont);
                } return _fontDescent;
            }
        }

        /// <summary>
        /// Gets the first word of the box
        /// </summary>
        internal CssBoxWord FirstWord
        {
            get { return Words[0]; }
        }

        /// <summary>
        /// Gets or sets the first linebox where content of this box appear
        /// </summary>
        internal CssLineBox FirstHostingLineBox
        {
            get { return _firstHostingLineBox; }
            set { _firstHostingLineBox = value; }
        }

        /// <summary>
        /// Gets or sets the last linebox where content of this box appear
        /// </summary>
        internal CssLineBox LastHostingLineBox
        {
            get { return _lastHostingLineBox; }
            set { _lastHostingLineBox = value; }
        }

        /// <summary>
        /// Gets the HTMLTag that hosts this box
        /// </summary>
        public HtmlTag HtmlTag
        {
            get { return _htmltag; }
        }

        /// <summary>
        /// Gets the InitialContainer of the Box.
        /// WARNING: May be null.
        /// </summary>
        public InitialContainer InitialContainer
        {
            get { return _initialContainer; }
        }

        /// <summary>
        /// Gets if this box represents an image
        /// </summary>
        public bool IsImage
        {
            get { return Words.Count == 1 && Words[0].IsImage; } 
        }

        /// <summary>
        /// Gets a value indicating if at least one of the corners of the box is rounded
        /// </summary>
        public bool IsRounded
        {
            get { return ActualCornerNE > 0f || ActualCornerNW > 0f || ActualCornerSE > 0f || ActualCornerSW > 0f; }
        }

        /// <summary>
        /// Tells if the box is empty or contains just blank spaces
        /// </summary>
        public bool IsSpaceOrEmpty
        {
            get {


                if ((Words.Count == 0 && Boxes.Count == 0) ||
                (Words.Count == 1 && Words[0].IsSpaces) ||
                Boxes.Count == 1 && Boxes[0] is CssAnonymousSpaceBlockBox) return true;

                foreach (CssBoxWord word in Words)
                {
                    if (!word.IsSpaces)
                    {
                        return false;
                    }
                }

                return true;
            }
        }

        /// <summary>
        /// Gets the last word of the box
        /// </summary>
        internal CssBoxWord LastWord
        {
            get { return Words[Words.Count - 1]; }
        }

        /// <summary>
        /// Gets the line-boxes of this box (if block box)
        /// </summary>
        internal List<CssLineBox> LineBoxes
        {
            get { return _lineBoxes; }
        }

        /// <summary>
        /// Gets or sets the location of the box
        /// </summary>
        public PointF Location
        {
            get { return _location; }
            set { _location = value; }
        }

        /// <summary>
        /// Gets or sets the parent box of this box
        /// </summary>
        public CssBox ParentBox
        {
            get { return _parentBox; }
            set 
            { 
                //Remove from last parent
                if (_parentBox != null && _parentBox.Boxes.Contains(this))
                {
                    _parentBox.Boxes.Remove(this);
                }

                _parentBox = value;

                //Add to new parent
                if (value != null && !value.Boxes.Contains(this))
                {
                    _parentBox.Boxes.Add(this);
                    _initialContainer = value.InitialContainer;
                }
            }
        }

        /// <summary>
        /// Gets the linebox(es) that contains words of this box (if inline)
        /// </summary>
        internal List<CssLineBox> ParentLineBoxes
        {
            get { return _parentLineBoxes; }
        }

        /// <summary>
        /// Gets the rectangles where this box should be painted
        /// </summary>
        internal Dictionary<CssLineBox, RectangleF> Rectangles
        {
            get
            {
                

                return _rectangles;
            }
        }

        /// <summary>
        /// Gets the right of the box. When setting, it will affect only the width of the box.
        /// </summary>
        public float ActualRight
        {
            get { return Location.X + Size.Width; }
            set
            {
                Size = new SizeF(value - Location.X, Size.Height);
            }
        }

        /// <summary>
        /// Gets or sets the size of the box
        /// </summary>
        public SizeF Size
        {
            get { return _size; }
            set { _size = value; }
        }

        /// <summary>
        /// Gets or sets the inner text of the box
        /// </summary>
        public string Text
        {
            get { return _text; }
            set { _text = value; UpdateWords(); }
        }

        /// <summary>
        /// Gets the BoxWords of text in the box
        /// </summary>
        internal List<CssBoxWord> Words
        {
            get { return _boxWords; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Sets the initial container of the box
        /// </summary>
        /// <param name="b"></param>
        private void SetInitialContainer(InitialContainer b)
        {
            _initialContainer = b;
        }

        /// <summary>
        /// Returns false if some of the boxes
        /// </summary>
        /// <returns></returns>
        internal bool ContainsInlinesOnly()
        {
            foreach (CssBox b in Boxes)
            {
                if (b.Display != CssConstants.Inline)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Gets the index of the box to be used on a (ordered) list
        /// </summary>
        /// <returns></returns>
        private int GetIndexForList()
        {
            int index = 0;

            foreach (CssBox b in ParentBox.Boxes)
            {
                if (b.Display == CssConstants.ListItem) index++;

                if (b.Equals(this)) return index;
            }

            return index;
        }

        /// <summary>
        /// Creates the <see cref="ListItemBox"/>
        /// </summary>
        /// <param name="g"></param>
        private void CreateListItemBox(Graphics g)
        {
            if (Display == CssConstants.ListItem)
            {
                if (_listItemBox == null)
                {
                    _listItemBox = new CssBox();
                    _listItemBox.InheritStyle(this, false);
                    _listItemBox.Display = CssConstants.Inline;
                    _listItemBox.SetInitialContainer(InitialContainer);

                    if (ParentBox != null && ListStyleType == CssConstants.Decimal)
                    {
                        _listItemBox.Text = GetIndexForList().ToString() + ".";
                    }
                    else
                    {
                        _listItemBox.Text = "�";
                    }
                    
                    _listItemBox.MeasureBounds(g);
                    _listItemBox.Size = new SizeF(_listItemBox.Words[0].Width, _listItemBox.Words[0].Height); 
                }
                _listItemBox.Words[0].Left = Location.X - _listItemBox.Size.Width - 5;
                _listItemBox.Words[0].Top = Location.Y + ActualPaddingTop;// +FontAscent;
            }
        }

        /// <summary>
        /// Searches for the first word occourence inside the box, on the specified linebox
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        internal CssBoxWord FirstWordOccourence(CssBox b, CssLineBox line)
        {
            if (b.Words.Count == 0 && b.Boxes.Count == 0)
            {
                return null;
            }

            if (b.Words.Count > 0)
            {   
                foreach (CssBoxWord word in b.Words)
                {
                    if (line.Words.Contains(word))
                    {
                        return word;
                    }
                }
                return null;
            }
            else
            {
                foreach (CssBox bb in b.Boxes)
                {
                    CssBoxWord w = FirstWordOccourence(bb, line);

                    if (w != null)
                    {
                        return w;
                    }
                }

                return null;
            }
        }

        /// <summary>
        /// Gets the specified Attribute, returns string.Empty if no attribute specified
        /// </summary>
        /// <param name="attribute">Attribute to retrieve</param>
        /// <returns>Attribute value or string.Empty if no attribute specified</returns>
        internal string GetAttribute(string attribute)
        {
            return GetAttribute(attribute, string.Empty);
        }

        /// <summary>
        /// Gets the value of the specified attribute of the source HTML tag.
        /// </summary>
        /// <param name="attribute">Attribute to retrieve</param>
        /// <param name="defaultValue">Value to return if attribute is not specified</param>
        /// <returns>Attribute value or defaultValue if no attribute specified</returns>
        internal string GetAttribute(string attribute, string defaultValue)
        {
            if (HtmlTag == null)
            {
                return defaultValue;
            }

            if (!HtmlTag.HasAttribute(attribute))
            {
                return defaultValue;
            }

            return HtmlTag.Attributes[attribute];
        }

        /// <summary>
        /// Gets the height of the font in the specified units
        /// </summary>
        /// <param name="unit"></param>
        /// <returns></returns>
        public float GetEmHeight()
        {
            //float res = Convert.ToSingle(ActualFont.Height);
            //float res = ActualFont.Size * ActualFont.FontFamily.GetCellAscent(f.Style) / ActualFont.FontFamily.GetEmHeight(f.Style);
            float res = ActualFont.GetHeight();
            return res;
        }

        /// <summary>
        /// Gets the previous sibling of this box.
        /// </summary>
        /// <returns>Box before this one on the tree. Null if its the first</returns>
        private CssBox GetPreviousSibling(CssBox b)
        {
            if (b.ParentBox == null)
            {
                return null; //This is initial containing block
            }

            int index = b.ParentBox.Boxes.IndexOf(this);

            if (index < 0) throw new Exception("Box doesn't exist on parent's Box list");

            if (index == 0) return null; //This is the first sibling.


            int diff = 1;
            CssBox sib = b.ParentBox.Boxes[index - diff];

            while ((sib.Display == CssConstants.None || sib.Position == CssConstants.Absolute) && index - diff - 1 >= 0)
            {
                sib = b.ParentBox.Boxes[index - ++diff];
            }

            return sib.Display == CssConstants.None ? null : sib;
        }

        /// <summary>
        /// Gets the minimum width that the box can be.
        /// The box can be as thin as the longest word plus padding.
        /// The check is deep thru box tree.
        /// </summary>
        /// <returns></returns>
        internal float GetMinimumWidth()
        {
            float maxw = 0f;
            float padding = 0f;
            CssBoxWord word = null;

            GetMinimumWidth_LongestWord(this, ref maxw, ref word);

            if (word != null)
            {
                GetMinimumWidth_BubblePadding(word.OwnerBox, this, ref padding);
            }

            return maxw + padding;
        }

        /// <summary>
        /// Bubbles up the padding from the starting box
        /// </summary>
        /// <param name="box"></param>
        /// <returns></returns>
        private void GetMinimumWidth_BubblePadding(CssBox box, CssBox endbox, ref float sum)
        {
            //float padding = box.ActualMarginLeft + box.ActualBorderLeftWidth + box.ActualPaddingLeft +
            //    box.ActualMarginRight + box.ActualBorderRightWidth + box.ActualPaddingRight;

            float padding =  box.ActualBorderLeftWidth + box.ActualPaddingLeft +
                 box.ActualBorderRightWidth + box.ActualPaddingRight;

            sum += padding;

            if (!box.Equals(endbox))
            {
                GetMinimumWidth_BubblePadding(box.ParentBox, endbox, ref sum);
            }
        }

        /// <summary>
        /// Gets the longest word (in width) inside the box, deeply.
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        private void GetMinimumWidth_LongestWord(CssBox b, ref float maxw, ref CssBoxWord word)
        {

            if (b.Words.Count > 0)
            {
                foreach (CssBoxWord w in b.Words)
                {
                    if (w.FullWidth > maxw)
                    {
                        maxw = w.FullWidth;
                        word = w;
                    }
                }
            }
            else
            {
                foreach(CssBox bb in b.Boxes)
                    GetMinimumWidth_LongestWord(bb, ref maxw,ref word);
            }

        }

        /// <summary>
        /// Gets the maximum bottom of the boxes inside the startBox
        /// </summary>
        /// <param name="startBox"></param>
        /// <param name="currentMaxBottom"></param>
        /// <returns></returns>
        internal float GetMaximumBottom(CssBox startBox, float currentMaxBottom)
        {
            foreach (CssLineBox line in startBox.Rectangles.Keys)
            {
                currentMaxBottom = Math.Max(currentMaxBottom, startBox.Rectangles[line].Bottom);
            }

            foreach (CssBox b in startBox.Boxes)
            {
                currentMaxBottom = Math.Max(currentMaxBottom, b.ActualBottom);
                currentMaxBottom = Math.Max(currentMaxBottom, GetMaximumBottom(b, currentMaxBottom));
            }

            return currentMaxBottom;
        }

        /// <summary>
        /// Get the width of the box at full width (No line breaks)
        /// </summary>
        /// <returns></returns>
        internal float GetFullWidth(Graphics g)
        {
            float sum = 0f;
            float paddingsum = 0f;
            GetFullWidth_WordsWith(this, g, ref sum, ref paddingsum);

            return paddingsum + sum;
        }

        /// <summary>
        /// Gets the longest word (in width) inside the box, deeply.
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        private void GetFullWidth_WordsWith(CssBox b, Graphics g, ref float sum, ref float paddingsum)
        {
            if (b.Display != CssConstants.Inline)
            {
                sum = 0;
            }

            paddingsum += b.ActualBorderLeftWidth + b.ActualBorderRightWidth + b.ActualPaddingRight + b.ActualPaddingLeft;

            if (b.Words.Count > 0)
            {
                foreach (CssBoxWord word in b.Words)
                    sum += word.FullWidth;
            }
            else
            {
                foreach (CssBox bb in b.Boxes)
                {
                    GetFullWidth_WordsWith(bb, g, ref sum, ref paddingsum);
                }
            }

        }

        /// <summary>
        /// Gets the next sibling of this box.
        /// </summary>
        /// <returns>Box after this one on the tree. Null if its the last one.</returns>
        private CssBox GetNextSibling()
        {
            if (ParentBox == null)
            {
                return null; //This is initial containing block
            }

            int index = ParentBox.Boxes.IndexOf(this);

            if (index < 0) throw new Exception("Box doesn't exist on parent's Box list");

            if (index == ParentBox.Boxes.Count - 1) return null; //This is the last sibling

            return ParentBox.Boxes[index + 1];
        }

        /// <summary>
        /// Gets if this box has only inline siblings (including itself)
        /// </summary>
        /// <returns></returns>
        internal bool HasJustInlineSiblings()
        {
            if (ParentBox == null)
            {
                return false;
            }

            return ParentBox.ContainsInlinesOnly();
        }

        /// <summary>
        /// Gets the rectangles where inline box will be drawn. See Remarks for more info.
        /// </summary>
        /// <returns>Rectangles where content should be placed</returns>
        /// <remarks>
        /// Inline boxes can be splitted across different LineBoxes, that's why this method
        /// Delivers a rectangle for each LineBox related to this box, if inline.
        /// </remarks>

         /// <summary>
        /// Inherits inheritable values from parent.
        /// </summary>
        internal void InheritStyle()
        {
            InheritStyle(ParentBox, false);
        }

        /// <summary>
        /// Inherits inheritable values from specified box.
        /// </summary>
        /// <param name="everything">Set to true to inherit all CSS properties instead of only the ineritables</param>
        /// <param name="godfather">Box to inherit the properties</param>
        internal void InheritStyle(CssBox godfather, bool everything)
        {
            if (godfather != null)
            {
                IEnumerable<PropertyInfo> pps = everything ? _cssproperties : _inheritables;
                foreach (PropertyInfo prop in pps)
                {
                    prop.SetValue(this,
                        prop.GetValue(godfather, null),
                        null);
                }
            }
        }

        /// <summary>
        /// Gets the result of collapsing the vertical margins of the two boxes
        /// </summary>
        /// <param name="a">Superior box (checks for margin-bottom)</param>
        /// <param name="b">Inferior box (checks for margin-top)</param>
        /// <returns>Maximum of margins</returns>
        private float MarginCollapse(CssBox a, CssBox b)
        {
            
            return Math.Max(
                a == null ? 0 : a.ActualMarginBottom,
                b == null ? 0 : b.ActualMarginTop);
        }

        /// <summary>
        /// Measures the bounds of box and children, recursively.
        /// </summary>
        /// <param name="g">Device context to draw</param>
        /// <param name="layoutRect">Rectangle containing the fragment</param>
        public virtual void MeasureBounds(Graphics g)
        {
            if (Display == CssConstants.None) return;

            RectanglesReset();

            MeasureWordsSize(g);

            if (Display == CssConstants.Block || 
                Display == CssConstants.ListItem || 
                Display == CssConstants.Table || 
                Display == CssConstants.InlineTable ||
                Display == CssConstants.TableCell ||
                Display == CssConstants.None)
            {
                #region Measure Bounds
                if (Display != CssConstants.TableCell)
                {
                    CssBox prevSibling = GetPreviousSibling(this);
                    float left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth;
                    float top =
                        (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : 0) +
                        MarginCollapse(prevSibling, this) +
                        (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0);
                    Location = new PointF(left, top);
                    ActualBottom = top;
                }

                if (Display != CssConstants.TableCell &&
                    Display != CssConstants.Table) //Because their width and height are set by CssTable
                {
                    #region Set Width
                    //width at 100% (or auto)
                    float minwidth = GetMinimumWidth();
                    float width =
                        ContainingBlock.Size.Width
                        - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight
                        - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth
                        - ActualMarginLeft - ActualMarginRight - ActualBorderLeftWidth - ActualBorderRightWidth;

                    //Check width if not auto
                    if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width))
                    {
                        width = CssValue.ParseLength(Width, width, this);
                    }

                    if (width < minwidth) width = minwidth;

                    Size = new SizeF(width, Size.Height);

                    #endregion
                }

                //If we're talking about a table here..
                if (Display == CssConstants.Table || Display == CssConstants.InlineTable)
                {
                    CssTable tbl = new CssTable(this, g);
                }
                else if (Display != CssConstants.None)
                {
                    //If there's just inlines, create LineBoxes
                    if (ContainsInlinesOnly())
                    {
                        ActualBottom = Location.Y;
                        CssLayoutEngine.CreateLineBoxes(g, this); //This will automatically set the bottom of this block
                    }
                    else
                    {
                        CssBox lastOne = null; // Boxes[Boxes.Count - 1];

                        //Treat as BlockBox
                        foreach (CssBox box in Boxes)
                        {
                            if (box.Display == CssConstants.None) continue;
                            //box.Display = CssConstants.Block; //Force to be block, according to CSS spec
                            box.MeasureBounds(g);
                            lastOne = box;
                        }

                        if (lastOne != null)
                            ActualBottom = Math.Max(ActualBottom, lastOne.ActualBottom + lastOne.ActualMarginBottom + ActualPaddingBottom);
                    }
                } 
                #endregion
            }

            if (InitialContainer != null)
            {
                InitialContainer.MaximumSize = new SizeF(
                    Math.Max(InitialContainer.MaximumSize.Width, ActualRight),
                    Math.Max(InitialContainer.MaximumSize.Height, ActualBottom));
            }
        }

        /// <summary>
        /// Measures the word spacing
        /// </summary>
        /// <param name="g"></param>
        private void MeasureWordSpacing(Graphics g)
        {
            _actualWordSpacing = CssLayoutEngine.WhiteSpace(g, this);

            if (WordSpacing != CssConstants.Normal)
            {
                string len = Parser.Search(Parser.CssLength, WordSpacing);

                _actualWordSpacing += CssValue.ParseLength(len, 1, this);
            }
        }

        /// <summary>
        /// Assigns words its width and height
        /// </summary>
        /// <param name="g"></param>
        internal void MeasureWordsSize(Graphics g)
        {
            if (_wordsSizeMeasured) return;

            //Measure white space if not yet done
            if (float.IsNaN(_actualWordSpacing))
                MeasureWordSpacing(g);

            if (HtmlTag != null && HtmlTag.TagName.Equals("img", StringComparison.CurrentCultureIgnoreCase))
            {
                #region Measure image

                CssBoxWord word = new CssBoxWord(this, CssValue.GetImage(GetAttribute("src")));
                Words.Clear();
                Words.Add(word);

                #endregion
            }
            else
            {
                #region Measure text words

                bool lastWasSpace = false;

                foreach (CssBoxWord b in Words)
                {
                    bool collapse = CssBoxWordSplitter.CollapsesWhiteSpaces(this);
                    if (CssBoxWordSplitter.EliminatesLineBreaks(this)) b.ReplaceLineBreaksAndTabs();

                    if (b.IsSpaces)
                    {
                        b.Height = FontLineSpacing;

                        if (b.IsTab)
                        {
                            b.Width = ActualWordSpacing * 4; //TODO: Configure tab size
                        }
                        else if (b.IsLineBreak)
                        {
                            b.Width = 0;
                        }
                        else
                        {
                            if (!(lastWasSpace && collapse))
                            {
                                b.Width = ActualWordSpacing * (collapse ? 1 : b.Text.Length);
                            }
                        }

                        lastWasSpace = true;
                    }
                    else
                    {
                        string word = b.Text;

                        CharacterRange[] measurable = { new CharacterRange(0, word.Length) };
                        StringFormat sf = new StringFormat();

                        sf.SetMeasurableCharacterRanges(measurable);

                        Region[] regions = g.MeasureCharacterRanges(word, ActualFont,
                            new RectangleF(0, 0, float.MaxValue, float.MaxValue),
                            sf);

                        SizeF s = regions[0].GetBounds(g).Size;
                        PointF p = regions[0].GetBounds(g).Location;

                        b.LastMeasureOffset = new PointF(p.X, p.Y);
                        b.Width = s.Width;// +p.X;
                        b.Height = s.Height;// +p.Y;

                        lastWasSpace = false;
                    }
                }
                #endregion
            }

            _wordsSizeMeasured = true;
        }

        /// <summary>
        /// Ensures that the specified length is converted to pixels if necessary
        /// </summary>
        /// <param name="length"></param>
        private string NoEms(string length)
        {
            CssLength len = new CssLength(length);

            if (len.Unit == CssLength.CssUnit.Ems)
            {
                length = len.ConvertEmToPixels(GetEmHeight()).ToString();
            }

            return length;
        }

        /// <summary>
        /// Deeply offsets the top of the box and its contents
        /// </summary>
        /// <param name="amount"></param>
        internal void OffsetTop(float amount)
        {
            List<CssLineBox> lines = new List<CssLineBox>();
            foreach (CssLineBox line in Rectangles.Keys)
                lines.Add(line);

            foreach (CssLineBox line in lines)
            {
                RectangleF r = Rectangles[line];
                Rectangles[line] = new RectangleF(r.X, r.Y + amount, r.Width, r.Height);
            }

            foreach (CssBoxWord word in Words)
            {
                word.Top += amount;
            }
            
            foreach (CssBox b in Boxes)
            {
                b.OffsetTop(amount);
            }
            //TODO: Aqu� me quede: no se mueve bien todo (probar con las tablas rojas)
            Location = new PointF(Location.X, Location.Y + amount);
        }

        /// <summary>
        /// Paints the fragment
        /// </summary>
        /// <param name="g"></param>
        public void Paint(Graphics g)
        {
            if (Display == CssConstants.None) 
                return;

            if (Display == CssConstants.TableCell && 
                EmptyCells == CssConstants.Hide &&
                IsSpaceOrEmpty)
                return;

            List<RectangleF> areas = Rectangles.Count == 0 ?
                new List<RectangleF>(new RectangleF[] { Bounds }) :
                new List<RectangleF>(Rectangles.Values);

            RectangleF[] rects = areas.ToArray();
            PointF offset = InitialContainer != null ? InitialContainer.ScrollOffset : PointF.Empty;

            for (int i = 0; i < rects.Length; i++)
            {
                RectangleF actualRect = rects[i]; actualRect.Offset(offset);

                if (InitialContainer != null && HtmlTag != null && HtmlTag.TagName.Equals("a", StringComparison.CurrentCultureIgnoreCase))
                {
                    if (InitialContainer.LinkRegions.ContainsKey(this)) InitialContainer.LinkRegions.Remove(this);
                    
                    InitialContainer.LinkRegions.Add(this, actualRect);
                }

                PaintBackground(g, actualRect);
                PaintBorder(g, actualRect, i == 0, i == rects.Length - 1);
            }

            if (IsImage)
            {
                RectangleF r = Words[0].Bounds; r.Offset(offset);
                r.Height -= ActualBorderTopWidth + ActualBorderBottomWidth + ActualPaddingTop + ActualPaddingBottom;
                r.Y += ActualBorderTopWidth + ActualPaddingTop;
                //HACK: round rectangle only when necessary
                g.DrawImage(Words[0].Image, Rectangle.Round(r));
            }
            else
            {
                Font f = ActualFont;
                using (SolidBrush b = new SolidBrush(CssValue.GetActualColor(Color)))
                {
                    foreach (CssBoxWord word in Words)
                    {
                        g.DrawString(word.Text, f, b, word.Left - word.LastMeasureOffset.X + offset.X, word.Top + offset.Y);
                    }
                }

            }
            for (int i = 0; i < rects.Length; i++)
            {
                RectangleF actualRect = rects[i]; actualRect.Offset(offset);

                PaintDecoration(g, actualRect, i == 0, i == rects.Length - 1);
            }
            
            foreach (CssBox b in Boxes)
            {
                b.Paint(g);
            }

            CreateListItemBox(g);

            if (ListItemBox != null)
            {
                ListItemBox.Paint(g);
            }
        }

        /// <summary>
        /// Paints the border of the box
        /// </summary>
        /// <param name="g"></param>
        private void PaintBorder(Graphics g, RectangleF rectangle, bool isFirst, bool isLast)
        {

            SmoothingMode smooth = g.SmoothingMode;

            if (InitialContainer != null && !InitialContainer.AvoidGeometryAntialias && IsRounded)
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;
            }

            //Top border
            if (!(string.IsNullOrEmpty(BorderTopStyle) || BorderTopStyle == CssConstants.None))
            {
                using (SolidBrush b = new SolidBrush(ActualBorderTopColor))
                {
                    if (BorderTopStyle == CssConstants.Inset) b.Color = CssDrawingHelper.Darken(ActualBorderTopColor);
                    g.FillPath(b, CssDrawingHelper.GetBorderPath(CssDrawingHelper.Border.Top, this, rectangle, isFirst, isLast));
                }
            }


            if (isLast)
            {
                //Right Border
                if (!(string.IsNullOrEmpty(BorderRightStyle) || BorderRightStyle == CssConstants.None))
                {
                    using (SolidBrush b = new SolidBrush(ActualBorderRightColor))
                    {
                        if (BorderRightStyle == CssConstants.Outset) b.Color = CssDrawingHelper.Darken(ActualBorderRightColor);
                        g.FillPath(b, CssDrawingHelper.GetBorderPath(CssDrawingHelper.Border.Right, this, rectangle, isFirst, isLast));
                    }
                }
            }

            //Bottom border
            if (!(string.IsNullOrEmpty(BorderBottomStyle) || BorderBottomStyle == CssConstants.None))
            {
                using (SolidBrush b = new SolidBrush(ActualBorderBottomColor))
                {
                    if (BorderBottomStyle == CssConstants.Outset) b.Color = CssDrawingHelper.Darken(ActualBorderBottomColor);
                    g.FillPath(b, CssDrawingHelper.GetBorderPath(CssDrawingHelper.Border.Bottom, this, rectangle, isFirst, isLast));
                }
            }

            if (isFirst)
            {
                //Left Border
                if (!(string.IsNullOrEmpty(BorderLeftStyle) || BorderLeftStyle == CssConstants.None))
                {
                    using (SolidBrush b = new SolidBrush(ActualBorderLeftColor))
                    {
                        if (BorderLeftStyle == CssConstants.Inset) b.Color = CssDrawingHelper.Darken(ActualBorderLeftColor);
                        g.FillPath(b, CssDrawingHelper.GetBorderPath(CssDrawingHelper.Border.Left, this, rectangle, isFirst, isLast));
                    }
                }
            }

            g.SmoothingMode = smooth;

        }

        /// <summary>
        /// Paints the background of the box
        /// </summary>
        /// <param name="g"></param>
        private void PaintBackground(Graphics g, RectangleF rectangle)
        {
            //HACK: Background rectangles are being deactivated when justifying text.
            if (ContainingBlock.TextAlign == CssConstants.Justify) return;

            GraphicsPath roundrect = null;
            Brush b = null;
            SmoothingMode smooth = g.SmoothingMode;

            if (IsRounded)
            {
                roundrect = CssDrawingHelper.GetRoundRect(rectangle, ActualCornerNW, ActualCornerNE, ActualCornerSE, ActualCornerSW);
            }
            
            if (BackgroundGradient != CssConstants.None)
            {
                b = new LinearGradientBrush(rectangle, ActualBackgroundColor, ActualBackgroundGradient, ActualBackgroundGradientAngle);
            }
            else
            {
                b = new SolidBrush(ActualBackgroundColor);
            }

            if (InitialContainer != null && !InitialContainer.AvoidGeometryAntialias && IsRounded)
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;
            }

            if (roundrect != null)
            {
                g.FillPath(b, roundrect);
            }
            else
            {
                g.FillRectangle(b, rectangle);
            }

            g.SmoothingMode = smooth;

            if (roundrect != null) roundrect.Dispose();
            if (b != null) b.Dispose();
        }

        /// <summary>
        /// Paints the text decoration
        /// </summary>
        /// <param name="g"></param>
        private void PaintDecoration(Graphics g, RectangleF rectangle, bool isFirst, bool isLast)
        {
            if (string.IsNullOrEmpty(TextDecoration) || TextDecoration == CssConstants.None || IsImage) return;

            float desc = CssLayoutEngine.GetDescent(ActualFont);
            float asc = CssLayoutEngine.GetAscent(ActualFont);
            float y = 0f;

            if (TextDecoration == CssConstants.Underline)
            {
                y = rectangle.Bottom - desc;
            }
            else if (TextDecoration == CssConstants.LineThrough)
            {
                y = rectangle.Bottom - desc - asc / 2;
            }
            else if (TextDecoration == CssConstants.Overline)
            {
                y = rectangle.Bottom - desc - asc - 2;
            }

            y -= ActualPaddingBottom - ActualBorderBottomWidth;

            float x1 = rectangle.X;
            float x2 = rectangle.Right;

            if (isFirst) x1 += ActualPaddingLeft + ActualBorderLeftWidth;
            if (isLast) x2 -= ActualPaddingRight + ActualBorderRightWidth;

            g.DrawLine(new Pen(ActualColor), x1, y, x2, y);
        }

        /// <summary>
        /// Offsets the rectangle of the specified linebox by the specified gap,
        /// and goes deep for rectangles of children in that linebox.
        /// </summary>
        /// <param name="lineBox"></param>
        /// <param name="gap"></param>
        internal void OffsetRectangle(CssLineBox lineBox, float gap)
        {
            if (Rectangles.ContainsKey(lineBox))
            {
                RectangleF r = Rectangles[lineBox];
                Rectangles[lineBox] = new RectangleF(r.X, r.Y + gap, r.Width, r.Height);
            }

            //foreach (Box b in Boxes)
            //{
            //    b.OffsetRectangle(lineBox, gap);
            //}
        }

        /// <summary>
        /// Resets the <see cref="Rectangles"/> array
        /// </summary>
        internal void RectanglesReset()
        {
            _rectangles.Clear();
        }

        /// <summary>
        /// Removes boxes that are just blank spaces
        /// </summary>
        internal void RemoveAnonymousSpaces()
        {
            for (int i = 0; i < Boxes.Count; i++)
            {
                if (Boxes[i] is CssAnonymousSpaceBlockBox || Boxes[i] is CssAnonymousSpaceBox)
                {
                    Boxes.RemoveAt(i);
                    i--;
                }
            }
        }

        /// <summary>
        /// Sets the bounds of the box
        /// </summary>
        /// <param name="r"></param>
        public void SetBounds(Rectangle r)
        {
            SetBounds(new RectangleF(r.X, r.Y, r.Width, r.Height));
        }

        /// <summary>
        /// Sets the bounds of the box
        /// </summary>
        /// <param name="rectangle"></param>
        public void SetBounds(RectangleF rectangle)
        {
            Size = rectangle.Size;
            Location = rectangle.Location;
        }

        /// <summary>
        /// ToString override.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string t = GetType().Name;
            if (HtmlTag != null)
            {
                t = string.Format("<{0}>", HtmlTag.TagName);
            }

            if (ParentBox == null)
            {
                return "Initial Container";
            }
            else if (Display == CssConstants.Block)
            {
                return string.Format("{0} BlockBox {2}, Children:{1}",t,  Boxes.Count, FontSize);
            }
            else if (Display == CssConstants.None)
            {
                return string.Format("{0} None", t);
            }
            else
            {
                return string.Format("{0} {2}: {1}", t, Text, Display);
            }


            //return base.ToString();
        }

        /// <summary>
        /// Splits the text into words and saves the result
        /// </summary>
        private void UpdateWords()
        {

            Words.Clear();

            CssBoxWordSplitter splitter = new CssBoxWordSplitter(this, Text);
            splitter.SplitWords();

            Words.AddRange(splitter.Words);
        }

        
        #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