// THIS FILE IS PART OF SVG PROJECT
// THE SVG PROJECT IS AN OPENSOURCE LIBRARY LICENSED UNDER THE MS-PL License.
// COPYRIGHT (C) svg-net. ALL RIGHTS RESERVED.
// GITHUB: https://github.com/svg-net/SVG
using System;
using System.Globalization;
using System.Linq;
namespace AntdUI.Svg
{
///
/// Represents a unit in an Scalable Vector Graphics document.
///
public struct SvgUnit
{
private SvgUnitType _type;
private float _value;
private bool _isEmpty;
private float? _deviceValue;
///
/// Gets and empty .
///
public static readonly SvgUnit Empty = new SvgUnit(SvgUnitType.User, 0) { _isEmpty = true };
///
/// Gets an with a value of none.
///
public static readonly SvgUnit None = new SvgUnit(SvgUnitType.None, 0f);
///
/// Gets a value to determine whether the unit is empty.
///
public bool IsEmpty
{
get { return _isEmpty; }
}
///
/// Gets whether this unit is none.
///
public bool IsNone
{
get { return _type == SvgUnitType.None; }
}
///
/// Gets the value of the unit.
///
public float Value
{
get { return _value; }
}
///
/// Gets the of unit.
///
public SvgUnitType Type
{
get { return _type; }
}
///
/// Converts the current unit to one that can be used at render time.
///
/// The representation of the current unit in a device value (usually pixels).
public float ToDeviceValue(ISvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
{
// If it's already been calculated
if (_deviceValue.HasValue)
{
return _deviceValue.Value;
}
if (_value == 0.0f)
{
_deviceValue = 0.0f;
return _deviceValue.Value;
}
// http://www.w3.org/TR/CSS21/syndata.html#values
// http://www.w3.org/TR/SVG11/coords.html#Units
const float cmInInch = 2.54f;
int ppi = SvgDocument.Ppi;
var type = Type;
var value = Value;
float points;
switch (type)
{
case SvgUnitType.Em:
using (var currFont = GetFont(renderer, owner))
{
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points / 72.0f) * ppi;
}
else
{
_deviceValue = value * (currFont.SizeInPoints / 72.0f) * ppi;
}
}
break;
case SvgUnitType.Ex:
using (var currFont = GetFont(renderer, owner))
{
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points * 0.5f / 72.0f) * ppi;
}
else
{
_deviceValue = value * 0.5f * (currFont.SizeInPoints / 72.0f) * ppi;
}
break;
}
case SvgUnitType.Centimeter:
_deviceValue = (float)((value / cmInInch) * ppi);
break;
case SvgUnitType.Inch:
_deviceValue = value * ppi;
break;
case SvgUnitType.Millimeter:
_deviceValue = (float)((value / 10) / cmInInch) * ppi;
break;
case SvgUnitType.Pica:
_deviceValue = ((value * 12) / 72) * ppi;
break;
case SvgUnitType.Point:
_deviceValue = (value / 72) * ppi;
break;
case SvgUnitType.Pixel:
_deviceValue = value;
break;
case SvgUnitType.User:
_deviceValue = value;
break;
case SvgUnitType.Percentage:
// Can't calculate if there is no style owner
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.GetBoundable());
if (boundable == null)
{
_deviceValue = value;
break;
}
System.Drawing.SizeF size = boundable.Bounds.Size;
switch (renderType)
{
case UnitRenderingType.Horizontal:
_deviceValue = (size.Width / 100) * value;
break;
case UnitRenderingType.HorizontalOffset:
_deviceValue = (size.Width / 100) * value + boundable.Location.X;
break;
case UnitRenderingType.Vertical:
_deviceValue = (size.Height / 100) * value;
break;
case UnitRenderingType.VerticalOffset:
_deviceValue = (size.Height / 100) * value + boundable.Location.Y;
break;
case UnitRenderingType.Other:
// Calculate a percentage value of the normalized viewBox diagonal length.
if (owner.OwnerDocument != null && owner.OwnerDocument.ViewBox != null && owner.OwnerDocument.ViewBox.Width != 0 && owner.OwnerDocument.ViewBox.Height != 0)
{
_deviceValue = (float)(Math.Sqrt(Math.Pow(owner.OwnerDocument.ViewBox.Width, 2) + Math.Pow(owner.OwnerDocument.ViewBox.Height, 2)) / Math.Sqrt(2) * value / 100.0);
}
else _deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0);
break;
}
break;
default:
_deviceValue = value;
break;
}
return _deviceValue.Value;
}
private IFontDefn GetFont(ISvgRenderer renderer, SvgElement owner)
{
if (owner == null) return null;
var visual = owner.Parents.OfType().FirstOrDefault();
return visual != null ? visual.GetFont(renderer) : null;
}
///
/// Converts the current unit to a percentage, if applicable.
///
/// An of type .
public SvgUnit ToPercentage()
{
switch (Type)
{
case SvgUnitType.Percentage:
return this;
case SvgUnitType.User:
return new SvgUnit(SvgUnitType.Percentage, Value * 100);
default:
throw new NotImplementedException();
}
}
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
if (obj == null) return false;
if (!(obj.GetType() == typeof(SvgUnit))) return false;
var unit = (SvgUnit)obj;
return (unit.Value == Value && unit.Type == Type);
}
public bool Equals(SvgUnit other)
{
return _type == other._type && (_value == other._value);
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked
{
hashCode += 1000000007 * _type.GetHashCode();
hashCode += 1000000009 * _value.GetHashCode();
hashCode += 1000000021 * _isEmpty.GetHashCode();
hashCode += 1000000033 * _deviceValue.GetHashCode();
}
return hashCode;
}
public static bool operator ==(SvgUnit lhs, SvgUnit rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(SvgUnit lhs, SvgUnit rhs)
{
return !(lhs == rhs);
}
#endregion
public override string ToString()
{
string type = string.Empty;
switch (Type)
{
case SvgUnitType.None:
return "none";
case SvgUnitType.Pixel:
type = "px";
break;
case SvgUnitType.Point:
type = "pt";
break;
case SvgUnitType.Inch:
type = "in";
break;
case SvgUnitType.Centimeter:
type = "cm";
break;
case SvgUnitType.Millimeter:
type = "mm";
break;
case SvgUnitType.Percentage:
type = "%";
break;
case SvgUnitType.Em:
type = "em";
break;
}
return string.Concat(Value.ToString(CultureInfo.InvariantCulture), type);
}
///
/// Performs an implicit conversion from to .
///
/// The value.
/// The result of the conversion.
public static implicit operator float(SvgUnit value)
{
return value.ToDeviceValue(null, UnitRenderingType.Other, null);
}
///
/// Performs an implicit conversion from to .
///
/// The value.
/// The result of the conversion.
public static implicit operator SvgUnit(float value)
{
return new SvgUnit(value);
}
///
/// Initializes a new instance of the struct.
///
/// The type.
/// The value.
public SvgUnit(SvgUnitType type, float value)
{
_isEmpty = false;
_type = type;
_value = value;
_deviceValue = null;
}
///
/// Initializes a new instance of the struct.
///
/// The value.
public SvgUnit(float value)
{
_isEmpty = false;
_value = value;
_type = SvgUnitType.User;
_deviceValue = null;
}
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, owner),
y.ToDeviceValue(renderer, UnitRenderingType.Vertical, owner));
}
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.SizeF(width.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
height.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
}
public enum UnitRenderingType
{
Other,
Horizontal,
HorizontalOffset,
Vertical,
VerticalOffset
}
///
/// Defines the various types of unit an can be.
///
public enum SvgUnitType
{
///
/// Indicates that the unit holds no value.
///
None,
///
/// Indicates that the unit is in pixels.
///
Pixel,
///
/// Indicates that the unit is equal to the pt size of the current font.
///
Em,
///
/// Indicates that the unit is equal to the x-height of the current font.
///
Ex,
///
/// Indicates that the unit is a percentage.
///
Percentage,
///
/// Indicates that the unit has no unit identifier and is a value in the current user coordinate system.
///
User,
///
/// Indicates the the unit is in inches.
///
Inch,
///
/// Indicates that the unit is in centimeters.
///
Centimeter,
///
/// Indicates that the unit is in millimeters.
///
Millimeter,
///
/// Indicates that the unit is in picas.
///
Pica,
///
/// Indicates that the unit is in points, the smallest unit of measure, being a subdivision of the larger . There are 12 points in the .
///
Point
}
}