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
}
}