using System; using System.Globalization; using TheArtOfDev.HtmlRenderer.Core.Parse; using TheArtOfDev.HtmlRenderer.Core.Utils; namespace TheArtOfDev.HtmlRenderer.Core.Dom { /// /// Represents and gets info about a CSS Length /// /// /// http://www.w3.org/TR/CSS21/syndata.html#length-units /// internal sealed class CssLength { #region Fields private readonly double _number; private readonly bool _isRelative; private readonly CssUnit _unit; private readonly string _length; private readonly bool _isPercentage; private readonly bool _hasError; #endregion /// /// Creates a new CssLength from a length specified on a CSS style sheet or fragment /// /// Length as specified in the Style Sheet or style fragment public CssLength(string length) { _length = length; _number = 0f; _unit = CssUnit.None; _isPercentage = false; //Return zero if no length specified, zero specified if (string.IsNullOrEmpty(length) || length == "0") return; //If percentage, use ParseNumber if (length.EndsWith("%")) { _number = CssValueParser.ParseNumber(length, 1); _isPercentage = true; return; } //If no units, has error if (length.Length < 3) { double.TryParse(length, out _number); _hasError = true; return; } //Get units of the length string u = length.Substring(length.Length - 2, 2); //Number of the length string number = length.Substring(0, length.Length - 2); //TODO: Units behave different in paper and in screen! switch (u) { case CssConstants.Em: _unit = CssUnit.Ems; _isRelative = true; break; case CssConstants.Ex: _unit = CssUnit.Ex; _isRelative = true; break; case CssConstants.Px: _unit = CssUnit.Pixels; _isRelative = true; break; case CssConstants.Mm: _unit = CssUnit.Milimeters; break; case CssConstants.Cm: _unit = CssUnit.Centimeters; break; case CssConstants.In: _unit = CssUnit.Inches; break; case CssConstants.Pt: _unit = CssUnit.Points; break; case CssConstants.Pc: _unit = CssUnit.Picas; break; default: _hasError = true; return; } if (!double.TryParse(number, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out _number)) { _hasError = true; } } #region Props /// /// Gets the number in the length /// public double Number { get { return _number; } } /// /// Gets if the length has some parsing error /// public bool HasError { get { return _hasError; } } /// /// Gets if the length represents a precentage (not actually a length) /// public bool IsPercentage { get { return _isPercentage; } } /// /// Gets if the length is specified in relative units /// public bool IsRelative { get { return _isRelative; } } /// /// Gets the unit of the length /// public CssUnit Unit { get { return _unit; } } /// /// Gets the length as specified in the string /// public string Length { get { return _length; } } #endregion #region Methods /// /// If length is in Ems, returns its value in points /// /// Em size factor to multiply /// Points size of this em /// If length has an error or isn't in ems public CssLength ConvertEmToPoints(double emSize) { if (HasError) throw new InvalidOperationException("Invalid length"); if (Unit != CssUnit.Ems) throw new InvalidOperationException("Length is not in ems"); return new CssLength(string.Format("{0}pt", Convert.ToSingle(Number * emSize).ToString("0.0", NumberFormatInfo.InvariantInfo))); } /// /// If length is in Ems, returns its value in pixels /// /// Pixel size factor to multiply /// Pixels size of this em /// If length has an error or isn't in ems public CssLength ConvertEmToPixels(double pixelFactor) { if (HasError) throw new InvalidOperationException("Invalid length"); if (Unit != CssUnit.Ems) throw new InvalidOperationException("Length is not in ems"); return new CssLength(string.Format("{0}px", Convert.ToSingle(Number * pixelFactor).ToString("0.0", NumberFormatInfo.InvariantInfo))); } /// /// Returns the length formatted ready for CSS interpreting. /// /// public override string ToString() { if (HasError) { return string.Empty; } else if (IsPercentage) { return string.Format(NumberFormatInfo.InvariantInfo, "{0}%", Number); } else { string u = string.Empty; switch (Unit) { case CssUnit.None: break; case CssUnit.Ems: u = "em"; break; case CssUnit.Pixels: u = "px"; break; case CssUnit.Ex: u = "ex"; break; case CssUnit.Inches: u = "in"; break; case CssUnit.Centimeters: u = "cm"; break; case CssUnit.Milimeters: u = "mm"; break; case CssUnit.Points: u = "pt"; break; case CssUnit.Picas: u = "pc"; break; } return string.Format(NumberFormatInfo.InvariantInfo, "{0}{1}", Number, u); } } #endregion } }