#region MigraDoc - Creating Documents on the Fly // // Authors: // Stefan Lange // Klaus Potzesny // David Stephensen // // Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://www.migradoc.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 MigraDoc.DocumentObjectModel.publics; namespace MigraDoc.DocumentObjectModel { /// /// A Unit consists of a numerical value and a UnitType like Centimeter, Millimeter, or Inch. /// Several conversions between different measures are supported. /// public struct Unit : IFormattable, INullableValue { /// /// Initializes a new instance of the Unit class with type set to point. /// public Unit(double point) { _value = (float)point; _type = UnitType.Point; _initialized = true; } /// /// Initializes a new instance of the Unit class. /// Throws System.ArgumentException if type is invalid. /// public Unit(double value, UnitType type) { if (!Enum.IsDefined(typeof(UnitType), type)) throw new /*InvalidEnum*/ArgumentException(DomSR.InvalidEnumValue(type), "type"); _value = (float)value; _type = type; _initialized = true; } /// /// Determines whether this instance is empty. /// public bool IsEmpty { get { return IsNull; } } /// /// Gets the value of the unit. /// object INullableValue.GetValue() { return this; } /// /// Sets the unit to the given value. /// void INullableValue.SetValue(object value) { if (value == null) throw new ArgumentNullException("value"); if (value is Unit) this = (Unit)value; else this = value.ToString(); } /// /// Resets this instance, /// i.e. IsNull() will return true afterwards. /// void INullableValue.SetNull() { _value = 0; _type = UnitType.Point; _initialized = false; } // Explicit interface implementations cannot contain access specifiers, i.e. they are accessible by a // cast operator only, e.g. ((IDomValue)obj).IsNull. // Therefore the second IsNull-Property is used as a handy shortcut. /// /// Determines whether this instance is null (not set). /// bool INullableValue.IsNull { get { return IsNull; } } /// /// Determines whether this instance is null (not set). /// public bool IsNull { get { return !_initialized; } } #region Properties /// /// Gets or sets the raw value of the object without any conversion. /// To determine the UnitType use property Type. /// public double Value { get { return (IsNull ? 0 : _value); } set { _value = (float)value; _initialized = true; } } /// /// Gets the UnitType of the object. /// public UnitType Type { get { return _type; } set { _type = value; } } /// /// Gets or sets the value in point. /// public double Point { get { if (IsNull) return 0; switch (_type) { case UnitType.Centimeter: return _value * 72 / 2.54; case UnitType.Inch: return _value * 72; case UnitType.Millimeter: return _value * 72 / 25.4; case UnitType.Pica: return _value * 12; case UnitType.Point: return _value; default: Debug.Assert(false, "Missing unit type."); return 0; } } set { _value = (float)value; _type = UnitType.Point; _initialized = true; } } //[Obsolete("Use Point")] //public double Pt //{ // get { return Point; } // set { Point = value; } //} /// /// Gets or sets the value in centimeter. /// public double Centimeter { get { if (IsNull) return 0; switch (_type) { case UnitType.Centimeter: return _value; case UnitType.Inch: return _value * 2.54; case UnitType.Millimeter: return _value / 10; case UnitType.Pica: return _value * 12 * 2.54 / 72; case UnitType.Point: return _value * 2.54 / 72; default: Debug.Assert(false, "Missing unit type"); return 0; } } set { _value = (float)value; _type = UnitType.Centimeter; _initialized = true; } } //[Obsolete("Use Centimeter")] //public double Cm //{ // get { return Centimeter; } // set { Centimeter = value; } //} /// /// Gets or sets the value in inch. /// public double Inch { get { if (IsNull) return 0; switch (_type) { case UnitType.Centimeter: return _value / 2.54; case UnitType.Inch: return _value; case UnitType.Millimeter: return _value / 25.4; case UnitType.Pica: return _value * 12 / 72; case UnitType.Point: return _value / 72; default: Debug.Assert(false, "Missing unit type"); return 0; } } set { _value = (float)value; _type = UnitType.Inch; _initialized = true; } } //[Obsolete("Use Inch")] //public double In //{ // get { return Inch; } // set { Inch = value; } //} /// /// Gets or sets the value in millimeter. /// public double Millimeter { get { if (IsNull) return 0; switch (_type) { case UnitType.Centimeter: return _value * 10; case UnitType.Inch: return _value * 25.4; case UnitType.Millimeter: return _value; case UnitType.Pica: return _value * 12 * 25.4 / 72; case UnitType.Point: return _value * 25.4 / 72; default: Debug.Assert(false, "Missing unit type"); return 0; } } set { _value = (float)value; _type = UnitType.Millimeter; _initialized = true; } } //[Obsolete("Use Millimeter")] //public double Mm //{ // get { return Millimeter; } // set { Millimeter = value; } //} /// /// Gets or sets the value in pica. /// public double Pica { get { if (IsNull) return 0; switch (_type) { case UnitType.Centimeter: return _value * 72 / 2.54 / 12; case UnitType.Inch: return _value * 72 / 12; case UnitType.Millimeter: return _value * 72 / 25.4 / 12; case UnitType.Pica: return _value; case UnitType.Point: return _value / 12; default: Debug.Assert(false, "Missing unit type"); return 0; } } set { _value = (float)value; _type = UnitType.Pica; _initialized = true; } } //[Obsolete("Use Pica")] //public double Pc //{ // get { return Pica; } // set { Pica = value; } //} #endregion #region Methods /// /// Returns the object as string using the format information. /// Measure will be added to the end of the string. /// public string ToString(System.IFormatProvider formatProvider) { if (IsNull) return 0.ToString(formatProvider); string valuestring = _value.ToString(formatProvider) + GetSuffix(); return valuestring; } /// /// Returns the object as string using the format. /// Measure will be added to the end of the string. /// public string ToString(string format) { if (IsNull) return 0.ToString(format); string valuestring = _value.ToString(format) + GetSuffix(); return valuestring; } /// /// Returns the object as string using the specified format and format information. /// Measure will be added to the end of the string. /// string IFormattable.ToString(string format, IFormatProvider formatProvider) { if (IsNull) return 0.ToString(format, formatProvider); string valuestring = _value.ToString(format, formatProvider) + GetSuffix(); return valuestring; } /// /// Returns the object as string. Measure will be added to the end of the string. /// public override string ToString() { if (IsNull) return 0.ToString(System.Globalization.CultureInfo.InvariantCulture); string valuestring = _value.ToString(System.Globalization.CultureInfo.InvariantCulture) + GetSuffix(); return valuestring; } /// /// Returns the type of the object as a string like 'pc', 'cm', or 'in'. Empty string is equal to 'pt'. /// string GetSuffix() { switch (_type) { case UnitType.Centimeter: return "cm"; case UnitType.Inch: return "in"; case UnitType.Millimeter: return "mm"; case UnitType.Pica: return "pc"; case UnitType.Point: //Point is default, so leave this blank. return ""; default: Debug.Assert(false, "Missing unit type"); return ""; } } /// /// Returns a Unit object. Sets type to centimeter. /// public static Unit FromCentimeter(double value) { Unit unit = Unit.Zero; unit._value = (float)value; unit._type = UnitType.Centimeter; return unit; } //[Obsolete("Use FromCentimer")] //public static Unit FromCm(double value) //{ // return FromCentimeter(value); //} /// /// Returns a Unit object. Sets type to millimeter. /// public static Unit FromMillimeter(double value) { Unit unit = Unit.Zero; unit._value = (float)value; unit._type = UnitType.Millimeter; return unit; } ///// ///// Returns a Unit object. Sets type to millimeter. ///// //[Obsolete("Use FromMillimeter")] //public static Unit FromMm(double value) //{ // return FromMillimeter(value); //} /// /// Returns a Unit object. Sets type to point. /// public static Unit FromPoint(double value) { Unit unit = Unit.Zero; unit._value = (float)value; unit._type = UnitType.Point; return unit; } /// /// Returns a Unit object. Sets type to inch. /// public static Unit FromInch(double value) { Unit unit = Unit.Zero; unit._value = (float)value; unit._type = UnitType.Inch; return unit; } /// /// Returns a Unit object. Sets type to pica. /// public static Unit FromPica(double value) { Unit unit = Unit.Zero; unit._value = (float)value; unit._type = UnitType.Pica; return unit; } #endregion /// /// Converts a string to a Unit 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 Unit(string value) { Unit unit = Zero; value = value.Trim(); // 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; } unit._value = 1; try { unit._value = float.Parse(value.Substring(0, valLen).Trim(), System.Globalization.CultureInfo.InvariantCulture); } catch (FormatException ex) { throw new ArgumentException(DomSR.InvalidUnitValue(value), ex); } string typeStr = value.Substring(valLen).Trim().ToLower(); unit._type = UnitType.Point; switch (typeStr) { case "cm": unit._type = UnitType.Centimeter; break; case "in": unit._type = UnitType.Inch; break; case "mm": unit._type = UnitType.Millimeter; break; case "pc": unit._type = UnitType.Pica; break; case "": case "pt": unit._type = UnitType.Point; break; default: throw new ArgumentException(DomSR.InvalidUnitType(typeStr)); } return unit; } /// /// Converts an int to a Unit object with type set to point. /// public static implicit operator Unit(int value) { Unit unit = Zero; unit._value = value; unit._type = UnitType.Point; return unit; } /// /// Converts a float to a Unit object with type set to point. /// public static implicit operator Unit(float value) { Unit unit = Zero; unit._value = value; unit._type = UnitType.Point; return unit; } /// /// Converts a double to a Unit object with type set to point. /// public static implicit operator Unit(double value) { Unit unit = Zero; unit._value = (float)value; unit._type = UnitType.Point; return unit; } /// /// Returns a double value as point. /// public static implicit operator double(Unit value) { return value.Point; } /// /// Returns a float value as point. /// public static implicit operator float(Unit value) { return (float)value.Point; } /// /// Memberwise comparison. To compare by value, /// use code like Math.Abs(a.Point - b.Point) < 1e-5. /// public static bool operator ==(Unit l, Unit r) { // ReSharper disable CompareOfFloatsByEqualityOperator return (l._initialized == r._initialized && l._type == r._type && l._value == r._value); // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// Memberwise comparison. To compare by value, /// use code like Math.Abs(a.Point - b.Point) < 1e-5. /// public static bool operator !=(Unit l, Unit r) { return !(l == r); } /// /// Calls base class Equals. /// public override bool Equals(Object obj) { return base.Equals(obj); } /// /// Calls base class GetHashCode. /// public override int GetHashCode() { return base.GetHashCode(); } /// /// This member is intended to be used by XmlDomainObjectReader only. /// public static Unit Parse(string value) { Unit unit = Zero; unit = value; return unit; } /// /// Converts an existing object from one unit into another unit type. /// public void ConvertType(UnitType type) { if (_type == type) return; switch (type) { case UnitType.Centimeter: _value = (float)Centimeter; _type = UnitType.Centimeter; break; case UnitType.Inch: _value = (float)Inch; _type = UnitType.Inch; break; case UnitType.Millimeter: _value = (float)Millimeter; _type = UnitType.Millimeter; break; case UnitType.Pica: _value = (float)Pica; _type = UnitType.Pica; break; case UnitType.Point: _value = (float)Point; _type = UnitType.Point; break; default: if (!Enum.IsDefined(typeof(UnitType), type)) throw new ArgumentException(DomSR.InvalidUnitType(type.ToString())); // Remember missing unit type. Debug.Assert(false, "Missing unit type"); break; } } /// /// Represents the uninitialized Unit object. /// public static readonly Unit Empty = new Unit(); /// /// Represents an initialized Unit object with value 0 and unit type point. /// public static readonly Unit Zero = new Unit(0); /// /// Represents the uninitialized Unit object. Same as Unit.Empty. /// public static readonly Unit NullValue = Empty; bool _initialized; float _value; UnitType _type; } }