using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Globalization;
namespace System.Drawing.Html
{
public static class CssValue
{
/// <summary>
/// Evals a number and returns it. If number is a percentage, it will be multiplied by <see cref="hundredPercent"/>
/// </summary>
/// <param name="number">Number to be parsed</param>
/// <param name="factor">Number that represents the 100% if parsed number is a percentage</param>
/// <returns>Parsed number. Zero if error while parsing.</returns>
public static float ParseNumber(string number, float hundredPercent)
{
if (string.IsNullOrEmpty(number))
{
return 0f;
}
string toParse = number;
bool isPercent = number.EndsWith("%");
float result = 0f;
if (isPercent) toParse = number.Substring(0, number.Length - 1);
if (!float.TryParse(toParse, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out result))
{
return 0f;
}
if (isPercent)
{
result = (result / 100f) * hundredPercent;
}
return result;
}
/// <summary>
/// Parses a length. Lengths are followed by an unit identifier (e.g. 10px, 3.1em)
/// </summary>
/// <param name="length">Specified length</param>
/// <param name="hundredPercent">Equivalent to 100 percent when length is percentage</param>
/// <param name="box"></param>
/// <returns></returns>
public static float ParseLength(string length, float hundredPercent, CssBox box)
{
return ParseLength(length, hundredPercent, box, box.GetEmHeight(), false);
}
/// <summary>
/// Parses a length. Lengths are followed by an unit identifier (e.g. 10px, 3.1em)
/// </summary>
/// <param name="length">Specified length</param>
/// <param name="hundredPercent">Equivalent to 100 percent when length is percentage</param>
/// <param name="box"></param>
/// <param name="useParentsEm"></param>
/// <param name="returnPoints">Allows the return float to be in points. If false, result will be pixels</param>
/// <returns></returns>
public static float ParseLength(string length, float hundredPercent, CssBox box, float emFactor, bool returnPoints)
{
//Return zero if no length specified, zero specified
if (string.IsNullOrEmpty(length) || length == "0") return 0f;
//If percentage, use ParseNumber
if (length.EndsWith("%")) return ParseNumber(length, hundredPercent);
//If no units, return zero
if (length.Length < 3) return 0f;
//Get units of the length
string unit = length.Substring(length.Length - 2, 2);
//Factor will depend on the unit
float factor = 1f;
//Number of the length
string number = length.Substring(0, length.Length - 2);
//TODO: Units behave different in paper and in screen!
switch (unit)
{
case CssConstants.Em:
factor = emFactor;
break;
case CssConstants.Px:
factor = 1f;
break;
case CssConstants.Mm:
factor = 3f; //3 pixels per millimeter
break;
case CssConstants.Cm:
factor = 37f; //37 pixels per centimeter
break;
case CssConstants.In:
factor = 96f; //96 pixels per inch
break;
case CssConstants.Pt:
factor = 96f / 72f; // 1 point = 1/72 of inch
if (returnPoints)
{
return ParseNumber(number, hundredPercent);
}
break;
case CssConstants.Pc:
factor = 96f / 72f * 12f; // 1 pica = 12 points
break;
default:
factor = 0f;
break;
}
return factor * ParseNumber(number, hundredPercent);
}
/// <summary>
/// Parses a color value in CSS style; e.g. #ff0000, red, rgb(255,0,0), rgb(100%, 0, 0)
/// </summary>
/// <param name="colorValue">Specified color value; e.g. #ff0000, red, rgb(255,0,0), rgb(100%, 0, 0)</param>
/// <returns>System.Drawing.Color value</returns>
public static Color GetActualColor(string colorValue)
{
int r = 0;
int g = 0;
int b = 0;
Color onError = Color.Empty;
if (string.IsNullOrEmpty(colorValue)) return onError;
colorValue = colorValue.ToLower().Trim();
if (colorValue.StartsWith("#"))
{
#region hexadecimal forms
string hex = colorValue.Substring(1);
if (hex.Length == 6)
{
r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
}
else if (hex.Length == 3)
{
r = int.Parse(new String(hex.Substring(0, 1)[0], 2), System.Globalization.NumberStyles.HexNumber);
g = int.Parse(new String(hex.Substring(1, 1)[0], 2), System.Globalization.NumberStyles.HexNumber);
b = int.Parse(new String(hex.Substring(2, 1)[0], 2), System.Globalization.NumberStyles.HexNumber);
}
else
{
return onError;
}
#endregion
}
else if (colorValue.StartsWith("rgb(") && colorValue.EndsWith(")"))
{
#region RGB forms
string rgb = colorValue.Substring(4, colorValue.Length - 5);
string[] chunks = rgb.Split(',');
if (chunks.Length == 3)
{
unchecked
{
r = Convert.ToInt32(ParseNumber(chunks[0].Trim(), 255f));
g = Convert.ToInt32(ParseNumber(chunks[1].Trim(), 255f));
b = Convert.ToInt32(ParseNumber(chunks[2].Trim(), 255f));
}
}
else
{
return onError;
}
#endregion
}
else
{
#region Color Constants
string hex = string.Empty;
switch (colorValue)
{
case CssConstants.Maroon:
hex = "#800000"; break;
case CssConstants.Red:
hex = "#ff0000"; break;
case CssConstants.Orange:
hex = "#ffA500"; break;
case CssConstants.Olive:
hex = "#808000"; break;
case CssConstants.Purple:
hex = "#800080"; break;
case CssConstants.Fuchsia:
hex = "#ff00ff"; break;
case CssConstants.White:
hex = "#ffffff"; break;
case CssConstants.Lime:
hex = "#00ff00"; break;
case CssConstants.Green:
hex = "#008000"; break;
case CssConstants.Navy:
hex = "#000080"; break;
case CssConstants.Blue:
hex = "#0000ff"; break;
case CssConstants.Aqua:
hex = "#00ffff"; break;
case CssConstants.Teal:
hex = "#008080"; break;
case CssConstants.Black:
hex = "#000000"; break;
case CssConstants.Silver:
hex = "#c0c0c0"; break;
case CssConstants.Gray:
hex = "#808080"; break;
case CssConstants.Yellow:
hex = "#FFFF00"; break;
}
if (string.IsNullOrEmpty(hex))
{
return onError;
}
else
{
Color c = GetActualColor(hex);
r = c.R;
g = c.G;
b = c.B;
}
#endregion
}
return Color.FromArgb(r, g, b);
}
/// <summary>
/// Parses a border value in CSS style; e.g. 1px, 1, thin, thick, medium
/// </summary>
/// <param name="borderValue"></param>
/// <returns></returns>
public static float GetActualBorderWidth(string borderValue, CssBox b)
{
if (string.IsNullOrEmpty(borderValue))
{
return GetActualBorderWidth(CssConstants.Medium, b);
}
switch (borderValue)
{
case CssConstants.Thin:
return 1f;
case CssConstants.Medium:
return 2f;
case CssConstants.Thick:
return 4f;
default:
return Math.Abs(ParseLength(borderValue, 1, b));
}
}
/// <summary>
/// Split the value by spaces; e.g. Useful in values like 'padding:5 4 3 inherit'
/// </summary>
/// <param name="value">Value to be splitted</param>
/// <returns>Splitted and trimmed values</returns>
public static string[] SplitValues(string value)
{
return SplitValues(value, ' ');
}
/// <summary>
/// Split the value by the specified separator; e.g. Useful in values like 'padding:5 4 3 inherit'
/// </summary>
/// <param name="value">Value to be splitted</param>
/// <returns>Splitted and trimmed values</returns>
public static string[] SplitValues(string value, char separator)
{
//TODO: CRITICAL! Don't split values on parenthesis (like rgb(0, 0, 0)) or quotes ("strings")
if (string.IsNullOrEmpty(value)) return new string[] { };
string[] values = value.Split(separator);
List<string> result = new List<string>();
for (int i = 0; i < values.Length; i++)
{
string val = values[i].Trim();
if (!string.IsNullOrEmpty(val))
{
result.Add(val);
}
}
return result.ToArray();
}
/// <summary>
/// Detects the type name in a path.
/// E.g. Gets System.Drawing.Graphics from a path like System.Drawing.Graphics.Clear
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private static Type GetTypeInfo(string path, ref string moreInfo)
{
int lastDot = path.LastIndexOf('.');
if (lastDot < 0) return null;
string type = path.Substring(0, lastDot);
moreInfo = path.Substring(lastDot + 1);
moreInfo = moreInfo.Replace("(", string.Empty).Replace(")", string.Empty);
foreach (Assembly a in HtmlRenderer.References)
{
Type t = a.GetType(type, false, true);
if (t != null) return t;
}
return null;
}
/// <summary>
/// Returns the object specific to the path
/// </summary>
/// <param name="path"></param>
/// <returns>One of the following possible objects: FileInfo, MethodInfo, PropertyInfo</returns>
private static object DetectSource(string path)
{
if (path.StartsWith("method:", StringComparison.CurrentCultureIgnoreCase))
{
string methodName = string.Empty;
Type t = GetTypeInfo(path.Substring(7), ref methodName); if (t == null) return null;
MethodInfo method = t.GetMethod(methodName);
if (!method.IsStatic || method.GetParameters().Length > 0)
{
return null;
}
return method;
}
else if (path.StartsWith("property:", StringComparison.CurrentCultureIgnoreCase))
{
string propName = string.Empty;
Type t = GetTypeInfo(path.Substring(9), ref propName); if (t == null) return null;
PropertyInfo prop = t.GetProperty(propName);
return prop;
}
else if (Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute))
{
return new Uri(path);
}
else
{
return new FileInfo(path);
}
}
/// <summary>
/// Gets the image of the specified path
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static Image GetImage(string path)
{
object source = DetectSource(path);
FileInfo finfo = source as FileInfo;
PropertyInfo prop = source as PropertyInfo;
MethodInfo method = source as MethodInfo;
try
{
if (finfo != null)
{
if (!finfo.Exists) return null;
return Image.FromFile(finfo.FullName);
}
else if (prop != null)
{
if (!prop.PropertyType.IsSubclassOf(typeof(Image)) && !prop.PropertyType.Equals(typeof(Image))) return null;
return prop.GetValue(null, null) as Image;
}
else if (method != null)
{
if (!method.ReturnType.IsSubclassOf(typeof(Image))) return null;
return method.Invoke(null, null) as Image;
}
else
{
return null;
}
}
catch
{
return new Bitmap(50, 50); //TODO: Return error image
}
}
/// <summary>
/// Gets the content of the stylesheet specified in the path
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string GetStyleSheet(string path)
{
object source = DetectSource(path);
FileInfo finfo = source as FileInfo;
PropertyInfo prop = source as PropertyInfo;
MethodInfo method = source as MethodInfo;
try
{
if (finfo != null)
{
if (!finfo.Exists) return null;
StreamReader sr = new StreamReader(finfo.FullName);
string result = sr.ReadToEnd();
sr.Dispose();
return result;
}
else if (prop != null)
{
if (!prop.PropertyType.Equals(typeof(string))) return null;
return prop.GetValue(null, null) as string;
}
else if (method != null)
{
if (!method.ReturnType.Equals(typeof(string))) return null;
return method.Invoke(null, null) as string;
}
else
{
return string.Empty;
}
}
catch
{
return string.Empty;
}
}
/// <summary>
/// Executes the desired action when the user clicks a link
/// </summary>
/// <param name="href"></param>
public static void GoLink(string href)
{
object source = DetectSource(href);
FileInfo finfo = source as FileInfo;
PropertyInfo prop = source as PropertyInfo;
MethodInfo method = source as MethodInfo;
Uri uri = source as Uri;
try
{
if (finfo != null || uri != null)
{
ProcessStartInfo nfo = new ProcessStartInfo(href);
nfo.UseShellExecute = true;
Process.Start(nfo);
}
else if (method != null)
{
method.Invoke(null, null);
}
else
{
//Nothing to do.
}
}
catch
{
throw;
}
}
}
}