#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; using System.Runtime.InteropServices; #if CORE #endif #if GDI using System.Drawing; using System.Drawing.Drawing2D; #endif #if WPF using System.Windows; using System.Windows.Media; using SysPoint = System.Windows.Point; using SysSize = System.Windows.Size; using SysRect = System.Windows.Rect; #endif #if NETFX_CORE using Windows.UI.Xaml.Media; using SysPoint = Windows.Foundation.Point; using SysSize = Windows.Foundation.Size; using SysRect = Windows.Foundation.Rect; #endif #if !EDF_CORE using PdfSharp.Internal; #else using PdfSharp.Internal; #endif namespace PdfSharp.Drawing { /// /// Stores a set of four floating-point numbers that represent the location and size of a rectangle. /// [DebuggerDisplay("{DebuggerDisplay}")] [Serializable, StructLayout(LayoutKind.Sequential)] // , ValueSerializer(typeof(RectValueSerializer)), TypeConverter(typeof(RectConverter))] public struct XRect : IFormattable { /// /// Initializes a new instance of the XRect class. /// public XRect(double x, double y, double width, double height) { if (width < 0 || height < 0) throw new ArgumentException("WidthAndHeightCannotBeNegative"); //SR.Get(SRID.Size_WidthAndHeightCannotBeNegative, new object[0])); _x = x; _y = y; _width = width; _height = height; } /// /// Initializes a new instance of the XRect class. /// public XRect(XPoint point1, XPoint point2) { _x = Math.Min(point1.X, point2.X); _y = Math.Min(point1.Y, point2.Y); _width = Math.Max(Math.Max(point1.X, point2.X) - _x, 0); _height = Math.Max(Math.Max(point1.Y, point2.Y) - _y, 0); } /// /// Initializes a new instance of the XRect class. /// public XRect(XPoint point, XVector vector) : this(point, point + vector) { } /// /// Initializes a new instance of the XRect class. /// public XRect(XPoint location, XSize size) { if (size.IsEmpty) this = s_empty; else { _x = location.X; _y = location.Y; _width = size.Width; _height = size.Height; } } /// /// Initializes a new instance of the XRect class. /// public XRect(XSize size) { if (size.IsEmpty) this = s_empty; else { _x = _y = 0; _width = size.Width; _height = size.Height; } } #if GDI /// /// Initializes a new instance of the XRect class. /// public XRect(PointF location, SizeF size) { _x = location.X; _y = location.Y; _width = size.Width; _height = size.Height; } #endif #if GDI /// /// Initializes a new instance of the XRect class. /// public XRect(RectangleF rect) { _x = rect.X; _y = rect.Y; _width = rect.Width; _height = rect.Height; } #endif #if WPF || NETFX_CORE /// /// Initializes a new instance of the XRect class. /// public XRect(SysRect rect) { _x = rect.X; _y = rect.Y; _width = rect.Width; _height = rect.Height; } #endif /// /// Creates a rectangle from for straight lines. /// // ReSharper disable InconsistentNaming public static XRect FromLTRB(double left, double top, double right, double bottom) // ReSharper restore InconsistentNaming { return new XRect(left, top, right - left, bottom - top); } /// /// Determines whether the two rectangles are equal. /// public static bool operator ==(XRect rect1, XRect rect2) { // ReSharper disable CompareOfFloatsByEqualityOperator return rect1.X == rect2.X && rect1.Y == rect2.Y && rect1.Width == rect2.Width && rect1.Height == rect2.Height; // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// Determines whether the two rectangles are not equal. /// public static bool operator !=(XRect rect1, XRect rect2) { return !(rect1 == rect2); } /// /// Determines whether the two rectangles are equal. /// public static bool Equals(XRect rect1, XRect rect2) { if (rect1.IsEmpty) return rect2.IsEmpty; return rect1.X.Equals(rect2.X) && rect1.Y.Equals(rect2.Y) && rect1.Width.Equals(rect2.Width) && rect1.Height.Equals(rect2.Height); } /// /// Determines whether this instance and the specified object are equal. /// public override bool Equals(object o) { if (!(o is XRect)) return false; return Equals(this, (XRect)o); } /// /// Determines whether this instance and the specified rect are equal. /// public bool Equals(XRect value) { return Equals(this, value); } /// /// Returns the hash code for this instance. /// public override int GetHashCode() { if (IsEmpty) return 0; return X.GetHashCode() ^ Y.GetHashCode() ^ Width.GetHashCode() ^ Height.GetHashCode(); } /// /// Parses the rectangle from a string. /// public static XRect Parse(string source) { XRect empty; CultureInfo cultureInfo = CultureInfo.InvariantCulture; TokenizerHelper helper = new TokenizerHelper(source, cultureInfo); string str = helper.NextTokenRequired(); if (str == "Empty") empty = Empty; else empty = new XRect(Convert.ToDouble(str, cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo)); helper.LastTokenRequired(); return empty; } /// /// Converts this XRect to a human readable string. /// public override string ToString() { return ConvertToString(null, null); } /// /// Converts this XRect to a human readable string. /// public string ToString(IFormatProvider provider) { return ConvertToString(null, provider); } /// /// Converts this XRect to a human readable string. /// string IFormattable.ToString(string format, IFormatProvider provider) { return ConvertToString(format, provider); } internal string ConvertToString(string format, IFormatProvider provider) { if (IsEmpty) return "Empty"; char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider); provider = provider ?? CultureInfo.InvariantCulture; // ReSharper disable FormatStringProblem return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}", new object[] { numericListSeparator, _x, _y, _width, _height }); // ReSharper restore FormatStringProblem } /// /// Gets the empty rectangle. /// public static XRect Empty { get { return s_empty; } } /// /// Gets a value indicating whether this instance is empty. /// public bool IsEmpty { get { return _width < 0; } } /// /// Gets or sets the location of the rectangle. /// public XPoint Location { get { return new XPoint(_x, _y); } set { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); _x = value.X; _y = value.Y; } } /// /// Gets or sets the size of the rectangle. /// //[Browsable(false)] public XSize Size { get { if (IsEmpty) return XSize.Empty; return new XSize(_width, _height); } set { if (value.IsEmpty) this = s_empty; else { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); _width = value.Width; _height = value.Height; } } } /// /// Gets or sets the X value of the rectangle. /// public double X { get { return _x; } set { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); _x = value; } } double _x; /// /// Gets or sets the Y value of the rectangle. /// public double Y { get { return _y; } set { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); _y = value; } } double _y; /// /// Gets or sets the width of the rectangle. /// public double Width { get { return _width; } set { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); if (value < 0) throw new ArgumentException("WidthCannotBeNegative"); //SR.Get(SRID.Size_WidthCannotBeNegative, new object[0])); _width = value; } } double _width; /// /// Gets or sets the height of the rectangle. /// public double Height { get { return _height; } set { if (IsEmpty) throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); if (value < 0) throw new ArgumentException("HeightCannotBeNegative"); //SR.Get(SRID.Size_HeightCannotBeNegative, new object[0])); _height = value; } } double _height; /// /// Gets the x-axis value of the left side of the rectangle. /// public double Left { get { return _x; } } /// /// Gets the y-axis value of the top side of the rectangle. /// public double Top { get { return _y; } } /// /// Gets the x-axis value of the right side of the rectangle. /// public double Right { get { if (IsEmpty) return double.NegativeInfinity; return _x + _width; } } /// /// Gets the y-axis value of the bottom side of the rectangle. /// public double Bottom { get { if (IsEmpty) return double.NegativeInfinity; return _y + _height; } } /// /// Gets the position of the top-left corner of the rectangle. /// public XPoint TopLeft { get { return new XPoint(Left, Top); } } /// /// Gets the position of the top-right corner of the rectangle. /// public XPoint TopRight { get { return new XPoint(Right, Top); } } /// /// Gets the position of the bottom-left corner of the rectangle. /// public XPoint BottomLeft { get { return new XPoint(Left, Bottom); } } /// /// Gets the position of the bottom-right corner of the rectangle. /// public XPoint BottomRight { get { return new XPoint(Right, Bottom); } } /// /// Gets the center of the rectangle. /// //[Browsable(false)] public XPoint Center { get { return new XPoint(_x + _width / 2, _y + _height / 2); } } /// /// Indicates whether the rectangle contains the specified point. /// public bool Contains(XPoint point) { return Contains(point.X, point.Y); } /// /// Indicates whether the rectangle contains the specified point. /// public bool Contains(double x, double y) { if (IsEmpty) return false; return ContainsInternal(x, y); } /// /// Indicates whether the rectangle contains the specified rectangle. /// public bool Contains(XRect rect) { return !IsEmpty && !rect.IsEmpty && _x <= rect._x && _y <= rect._y && _x + _width >= rect._x + rect._width && _y + _height >= rect._y + rect._height; } /// /// Indicates whether the specified rectangle intersects with the current rectangle. /// public bool IntersectsWith(XRect rect) { return !IsEmpty && !rect.IsEmpty && rect.Left <= Right && rect.Right >= Left && rect.Top <= Bottom && rect.Bottom >= Top; } /// /// Sets current rectangle to the intersection of the current rectangle and the specified rectangle. /// public void Intersect(XRect rect) { if (!IntersectsWith(rect)) this = Empty; else { double left = Math.Max(Left, rect.Left); double top = Math.Max(Top, rect.Top); _width = Math.Max(Math.Min(Right, rect.Right) - left, 0.0); _height = Math.Max(Math.Min(Bottom, rect.Bottom) - top, 0.0); _x = left; _y = top; } } /// /// Returns the intersection of two rectangles. /// public static XRect Intersect(XRect rect1, XRect rect2) { rect1.Intersect(rect2); return rect1; } /// /// Sets current rectangle to the union of the current rectangle and the specified rectangle. /// public void Union(XRect rect) { // ReSharper disable CompareOfFloatsByEqualityOperator if (IsEmpty) this = rect; else if (!rect.IsEmpty) { double left = Math.Min(Left, rect.Left); double top = Math.Min(Top, rect.Top); if (rect.Width == Double.PositiveInfinity || Width == Double.PositiveInfinity) _width = Double.PositiveInfinity; else { double right = Math.Max(Right, rect.Right); _width = Math.Max(right - left, 0.0); } if (rect.Height == Double.PositiveInfinity || _height == Double.PositiveInfinity) _height = Double.PositiveInfinity; else { double bottom = Math.Max(Bottom, rect.Bottom); _height = Math.Max(bottom - top, 0.0); } _x = left; _y = top; } // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// Returns the union of two rectangles. /// public static XRect Union(XRect rect1, XRect rect2) { rect1.Union(rect2); return rect1; } /// /// Sets current rectangle to the union of the current rectangle and the specified point. /// public void Union(XPoint point) { Union(new XRect(point, point)); } /// /// Returns the intersection of a rectangle and a point. /// public static XRect Union(XRect rect, XPoint point) { rect.Union(new XRect(point, point)); return rect; } /// /// Moves a rectangle by the specified amount. /// public void Offset(XVector offsetVector) { if (IsEmpty) throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); _x += offsetVector.X; _y += offsetVector.Y; } /// /// Moves a rectangle by the specified amount. /// public void Offset(double offsetX, double offsetY) { if (IsEmpty) throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); _x += offsetX; _y += offsetY; } /// /// Returns a rectangle that is offset from the specified rectangle by using the specified vector. /// public static XRect Offset(XRect rect, XVector offsetVector) { rect.Offset(offsetVector.X, offsetVector.Y); return rect; } /// /// Returns a rectangle that is offset from the specified rectangle by using specified horizontal and vertical amounts. /// public static XRect Offset(XRect rect, double offsetX, double offsetY) { rect.Offset(offsetX, offsetY); return rect; } /// /// Translates the rectangle by adding the specified point. /// //[Obsolete("Use Offset.")] public static XRect operator +(XRect rect, XPoint point) { return new XRect(rect._x + point.X, rect.Y + point.Y, rect._width, rect._height); } /// /// Translates the rectangle by subtracting the specified point. /// //[Obsolete("Use Offset.")] public static XRect operator -(XRect rect, XPoint point) { return new XRect(rect._x - point.X, rect.Y - point.Y, rect._width, rect._height); } /// /// Expands the rectangle by using the specified Size, in all directions. /// public void Inflate(XSize size) { Inflate(size.Width, size.Height); } /// /// Expands or shrinks the rectangle by using the specified width and height amounts, in all directions. /// public void Inflate(double width, double height) { if (IsEmpty) throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); _x -= width; _y -= height; _width += width; _width += width; _height += height; _height += height; if (_width < 0 || _height < 0) this = s_empty; } /// /// Returns the rectangle that results from expanding the specified rectangle by the specified Size, in all directions. /// public static XRect Inflate(XRect rect, XSize size) { rect.Inflate(size.Width, size.Height); return rect; } /// /// Creates a rectangle that results from expanding or shrinking the specified rectangle by the specified width and height amounts, in all directions. /// public static XRect Inflate(XRect rect, double width, double height) { rect.Inflate(width, height); return rect; } /// /// Returns the rectangle that results from applying the specified matrix to the specified rectangle. /// public static XRect Transform(XRect rect, XMatrix matrix) { XMatrix.MatrixHelper.TransformRect(ref rect, ref matrix); return rect; } /// /// Transforms the rectangle by applying the specified matrix. /// public void Transform(XMatrix matrix) { XMatrix.MatrixHelper.TransformRect(ref this, ref matrix); } /// /// Multiplies the size of the current rectangle by the specified x and y values. /// public void Scale(double scaleX, double scaleY) { if (!IsEmpty) { _x *= scaleX; _y *= scaleY; _width *= scaleX; _height *= scaleY; if (scaleX < 0) { _x += _width; _width *= -1.0; } if (scaleY < 0) { _y += _height; _height *= -1.0; } } } #if CORE // Internal version in CORE build. #if UseGdiObjects /// /// Converts this instance to a System.Drawing.RectangleF. /// internal RectangleF ToRectangleF() { return new RectangleF((float)_x, (float)_y, (float)_width, (float)_height); } #endif #endif #if GDI /// /// Converts this instance to a System.Drawing.RectangleF. /// public RectangleF ToRectangleF() { return new RectangleF((float)_x, (float)_y, (float)_width, (float)_height); } #endif #if GDI /// /// Performs an implicit conversion from a System.Drawing.Rectangle to an XRect. /// public static implicit operator XRect(Rectangle rect) { return new XRect(rect.X, rect.Y, rect.Width, rect.Height); } /// /// Performs an implicit conversion from a System.Drawing.RectangleF to an XRect. /// public static implicit operator XRect(RectangleF rect) { return new XRect(rect.X, rect.Y, rect.Width, rect.Height); } #endif #if WPF || NETFX_CORE /// /// Performs an implicit conversion from System.Windows.Rect to XRect. /// public static implicit operator XRect(SysRect rect) { return new XRect(rect.X, rect.Y, rect.Width, rect.Height); } #endif bool ContainsInternal(double x, double y) { return x >= _x && x - _width <= _x && y >= _y && y - _height <= _y; } static XRect CreateEmptyRect() { XRect rect = new XRect(); rect._x = double.PositiveInfinity; rect._y = double.PositiveInfinity; rect._width = double.NegativeInfinity; rect._height = double.NegativeInfinity; return rect; } static XRect() { s_empty = CreateEmptyRect(); } static readonly XRect s_empty; /// /// 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, "rect=({0:" + format + "}, {1:" + format + "}, {2:" + format + "}, {3:" + format + "})", _x, _y, _width, _height); } } } }