#region PDFsharp - A .NET library for processing PDF // // Authors: // Stefan Lange // // Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://sourceforge.net/projects/pdfsharp // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #endregion using System; using System.Diagnostics; using System.Globalization; namespace PdfSharp.Drawing { /// /// Represents a value and its unit of measure. The structure converts implicitly from and to /// double with a value measured in point. /// [DebuggerDisplay("{DebuggerDisplay}")] public struct XUnit : IFormattable { internal const double PointFactor = 1; internal const double InchFactor = 72; internal const double MillimeterFactor = 72 / 25.4; internal const double CentimeterFactor = 72 / 2.54; internal const double PresentationFactor = 72 / 96.0; internal const double PointFactorWpf = 96 / 72.0; internal const double InchFactorWpf = 96; internal const double MillimeterFactorWpf = 96 / 25.4; internal const double CentimeterFactorWpf = 96 / 2.54; internal const double PresentationFactorWpf = 1; /// /// Initializes a new instance of the XUnit class with type set to point. /// public XUnit(double point) { _value = point; _type = XGraphicsUnit.Point; } /// /// Initializes a new instance of the XUnit class. /// public XUnit(double value, XGraphicsUnit type) { if (!Enum.IsDefined(typeof(XGraphicsUnit), type)) #if !SILVERLIGHT && !NETFX_CORE && !UWP throw new System.ComponentModel.InvalidEnumArgumentException(nameof(type), (int)type, typeof(XGraphicsUnit)); #else throw new ArgumentException("type"); #endif _value = value; _type = type; } /// /// Gets the raw value of the object without any conversion. /// To determine the XGraphicsUnit use property Type. /// To get the value in point use the implicit conversion to double. /// public double Value { get { return _value; } } /// /// Gets the unit of measure. /// public XGraphicsUnit Type { get { return _type; } } /// /// Gets or sets the value in point. /// public double Point { get { switch (_type) { case XGraphicsUnit.Point: return _value; case XGraphicsUnit.Inch: return _value * 72; case XGraphicsUnit.Millimeter: return _value * 72 / 25.4; case XGraphicsUnit.Centimeter: return _value * 72 / 2.54; case XGraphicsUnit.Presentation: return _value * 72 / 96; default: throw new InvalidCastException(); } } set { _value = value; _type = XGraphicsUnit.Point; } } /// /// Gets or sets the value in inch. /// public double Inch { get { switch (_type) { case XGraphicsUnit.Point: return _value / 72; case XGraphicsUnit.Inch: return _value; case XGraphicsUnit.Millimeter: return _value / 25.4; case XGraphicsUnit.Centimeter: return _value / 2.54; case XGraphicsUnit.Presentation: return _value / 96; default: throw new InvalidCastException(); } } set { _value = value; _type = XGraphicsUnit.Inch; } } /// /// Gets or sets the value in millimeter. /// public double Millimeter { get { switch (_type) { case XGraphicsUnit.Point: return _value * 25.4 / 72; case XGraphicsUnit.Inch: return _value * 25.4; case XGraphicsUnit.Millimeter: return _value; case XGraphicsUnit.Centimeter: return _value * 10; case XGraphicsUnit.Presentation: return _value * 25.4 / 96; default: throw new InvalidCastException(); } } set { _value = value; _type = XGraphicsUnit.Millimeter; } } /// /// Gets or sets the value in centimeter. /// public double Centimeter { get { switch (_type) { case XGraphicsUnit.Point: return _value * 2.54 / 72; case XGraphicsUnit.Inch: return _value * 2.54; case XGraphicsUnit.Millimeter: return _value / 10; case XGraphicsUnit.Centimeter: return _value; case XGraphicsUnit.Presentation: return _value * 2.54 / 96; default: throw new InvalidCastException(); } } set { _value = value; _type = XGraphicsUnit.Centimeter; } } /// /// Gets or sets the value in presentation units (1/96 inch). /// public double Presentation { get { switch (_type) { case XGraphicsUnit.Point: return _value * 96 / 72; case XGraphicsUnit.Inch: return _value * 96; case XGraphicsUnit.Millimeter: return _value * 96 / 25.4; case XGraphicsUnit.Centimeter: return _value * 96 / 2.54; case XGraphicsUnit.Presentation: return _value; default: throw new InvalidCastException(); } } set { _value = value; _type = XGraphicsUnit.Point; } } /// /// Returns the object as string using the format information. /// The unit of measure is appended to the end of the string. /// public string ToString(IFormatProvider formatProvider) { string valuestring = _value.ToString(formatProvider) + GetSuffix(); return valuestring; } /// /// Returns the object as string using the specified format and format information. /// The unit of measure is appended to the end of the string. /// string IFormattable.ToString(string format, IFormatProvider formatProvider) { string valuestring = _value.ToString(format, formatProvider) + GetSuffix(); return valuestring; } /// /// Returns the object as string. The unit of measure is appended to the end of the string. /// public override string ToString() { string valuestring = _value.ToString(CultureInfo.InvariantCulture) + GetSuffix(); return valuestring; } /// /// Returns the unit of measure of the object as a string like 'pt', 'cm', or 'in'. /// string GetSuffix() { switch (_type) { case XGraphicsUnit.Point: return "pt"; case XGraphicsUnit.Inch: return "in"; case XGraphicsUnit.Millimeter: return "mm"; case XGraphicsUnit.Centimeter: return "cm"; case XGraphicsUnit.Presentation: return "pu"; //case XGraphicsUnit.Pica: // return "pc"; //case XGraphicsUnit.Line: // return "li"; default: throw new InvalidCastException(); } } /// /// Returns an XUnit object. Sets type to point. /// public static XUnit FromPoint(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Point; return unit; } /// /// Returns an XUnit object. Sets type to inch. /// public static XUnit FromInch(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Inch; return unit; } /// /// Returns an XUnit object. Sets type to millimeters. /// public static XUnit FromMillimeter(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Millimeter; return unit; } /// /// Returns an XUnit object. Sets type to centimeters. /// public static XUnit FromCentimeter(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Centimeter; return unit; } /// /// Returns an XUnit object. Sets type to Presentation. /// public static XUnit FromPresentation(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Presentation; return unit; } /// /// Converts a string to an XUnit object. /// If the string contains a suffix like 'cm' or 'in' the object will be converted /// to the appropriate type, otherwise point is assumed. /// public static implicit operator XUnit(string value) { XUnit unit; value = value.Trim(); // HACK for Germans... value = value.Replace(',', '.'); int count = value.Length; int valLen = 0; for (; valLen < count;) { char ch = value[valLen]; if (ch == '.' || ch == '-' || ch == '+' || char.IsNumber(ch)) valLen++; else break; } try { unit._value = Double.Parse(value.Substring(0, valLen).Trim(), CultureInfo.InvariantCulture); } catch (Exception ex) { unit._value = 1; string message = String.Format("String '{0}' is not a valid value for structure 'XUnit'.", value); throw new ArgumentException(message, ex); } string typeStr = value.Substring(valLen).Trim().ToLower(); unit._type = XGraphicsUnit.Point; switch (typeStr) { case "cm": unit._type = XGraphicsUnit.Centimeter; break; case "in": unit._type = XGraphicsUnit.Inch; break; case "mm": unit._type = XGraphicsUnit.Millimeter; break; case "": case "pt": unit._type = XGraphicsUnit.Point; break; case "pu": // presentation units unit._type = XGraphicsUnit.Presentation; break; default: throw new ArgumentException("Unknown unit type: '" + typeStr + "'"); } return unit; } /// /// Converts an int to an XUnit object with type set to point. /// public static implicit operator XUnit(int value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Point; return unit; } /// /// Converts a double to an XUnit object with type set to point. /// public static implicit operator XUnit(double value) { XUnit unit; unit._value = value; unit._type = XGraphicsUnit.Point; return unit; } /// /// Returns a double value as point. /// public static implicit operator double(XUnit value) { return value.Point; } /// /// Memberwise comparison. To compare by value, /// use code like Math.Abs(a.Pt - b.Pt) < 1e-5. /// public static bool operator ==(XUnit value1, XUnit value2) { // ReSharper disable CompareOfFloatsByEqualityOperator return value1._type == value2._type && value1._value == value2._value; // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// Memberwise comparison. To compare by value, /// use code like Math.Abs(a.Pt - b.Pt) < 1e-5. /// public static bool operator !=(XUnit value1, XUnit value2) { return !(value1 == value2); } /// /// Calls base class Equals. /// public override bool Equals(Object obj) { if (obj is XUnit) return this == (XUnit)obj; return false; } /// /// Returns the hash code for this instance. /// public override int GetHashCode() { // ReSharper disable NonReadonlyFieldInGetHashCode return _value.GetHashCode() ^ _type.GetHashCode(); // ReSharper restore NonReadonlyFieldInGetHashCode } /// /// This member is intended to be used by XmlDomainObjectReader only. /// public static XUnit Parse(string value) { XUnit unit = value; return unit; } /// /// Converts an existing object from one unit into another unit type. /// public void ConvertType(XGraphicsUnit type) { if (_type == type) return; switch (type) { case XGraphicsUnit.Point: _value = Point; _type = XGraphicsUnit.Point; break; case XGraphicsUnit.Inch: _value = Inch; _type = XGraphicsUnit.Inch; break; case XGraphicsUnit.Centimeter: _value = Centimeter; _type = XGraphicsUnit.Centimeter; break; case XGraphicsUnit.Millimeter: _value = Millimeter; _type = XGraphicsUnit.Millimeter; break; case XGraphicsUnit.Presentation: _value = Presentation; _type = XGraphicsUnit.Presentation; break; default: throw new ArgumentException("Unknown unit type: '" + type + "'"); } } /// /// Represents a unit with all values zero. /// public static readonly XUnit Zero = new XUnit(); double _value; XGraphicsUnit _type; /// /// Gets the DebuggerDisplayAttribute text. /// /// The debugger display. // ReSharper disable UnusedMember.Local string DebuggerDisplay // ReSharper restore UnusedMember.Local { get { const string format = Config.SignificantFigures10; return String.Format(CultureInfo.InvariantCulture, "unit=({0:" + format + "} {1})", _value, GetSuffix()); } } } }