#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);
}
}
}
}