#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; #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; #if !SILVERLIGHT using WpfBrushes = System.Windows.Media.Brushes; #endif #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 using PdfSharp.Internal; namespace PdfSharp.Drawing { /// /// Represents a series of connected lines and curves. /// public sealed class XGraphicsPath { /// /// Initializes a new instance of the class. /// public XGraphicsPath() { #if CORE _corePath = new CoreGraphicsPath(); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath = new GraphicsPath(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE _pathGeometry = new PathGeometry(); #endif } #if GDI /// /// Initializes a new instance of the class. /// public XGraphicsPath(PointF[] points, byte[] types, XFillMode fillMode) { #if GDI try { Lock.EnterGdiPlus(); _gdipPath = new GraphicsPath(points, types, (FillMode)fillMode); } finally { Lock.ExitGdiPlus(); } #endif #if WPF // Is true only in Hybrid build. _pathGeometry = new PathGeometry(); _pathGeometry.FillRule = FillRule.EvenOdd; #endif } #endif #if WPF || NETFX_CORE /// /// Gets the current path figure. /// PathFigure CurrentPathFigure { get { int count = _pathGeometry.Figures.Count; if (count == 0) { // Create new figure if there is none. _pathGeometry.Figures.Add(new PathFigure()); count++; } else { PathFigure lastFigure = _pathGeometry.Figures[count - 1]; if (lastFigure.IsClosed) { if (lastFigure.Segments.Count > 0) { // Create new figure if previous one was closed. _pathGeometry.Figures.Add(new PathFigure()); count++; } else { Debug.Assert(false); } } } // Return last figure in collection. return _pathGeometry.Figures[count - 1]; } } /// /// Gets the current path figure, but never created a new one. /// PathFigure PeekCurrentFigure { get { int count = _pathGeometry.Figures.Count; return count == 0 ? null : _pathGeometry.Figures[count - 1]; } } #endif /// /// Clones this instance. /// public XGraphicsPath Clone() { XGraphicsPath path = (XGraphicsPath)MemberwiseClone(); #if CORE _corePath = new CoreGraphicsPath(_corePath); #endif #if GDI try { Lock.EnterGdiPlus(); path._gdipPath = _gdipPath.Clone() as GraphicsPath; } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE path._pathGeometry = _pathGeometry.Clone(); #else // AG-HACK throw new InvalidOperationException("Silverlight cannot clone geometry objects."); // TODO: make it manually... #pragma warning disable 0162 #endif #endif return path; } // ----- AddLine ------------------------------------------------------------------------------ #if GDI /// /// Adds a line segment to current figure. /// public void AddLine(System.Drawing.Point pt1, System.Drawing.Point pt2) { AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); } #endif #if WPF /// /// Adds a line segment to current figure. /// public void AddLine(SysPoint pt1, SysPoint pt2) { AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); } #endif #if GDI /// /// Adds a line segment to current figure. /// public void AddLine(PointF pt1, PointF pt2) { AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); } #endif /// /// Adds a line segment to current figure. /// public void AddLine(XPoint pt1, XPoint pt2) { AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); } /// /// Adds a line segment to current figure. /// public void AddLine(double x1, double y1, double x2, double y2) { #if CORE _corePath.MoveOrLineTo(x1, y1); _corePath.LineTo(x2, y2, false); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddLine((float)x1, (float)y1, (float)x2, (float)y2); } finally { Lock.ExitGdiPlus(); } #endif #if WPF PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) { figure.StartPoint = new SysPoint(x1, y1); #if !SILVERLIGHT var lineSegment = new LineSegment(new SysPoint(x2, y2), true); #else var lineSegment = new LineSegment { Point = new Point(x2, y2) }; #endif figure.Segments.Add(lineSegment); } else { #if !SILVERLIGHT var lineSegment1 = new LineSegment(new SysPoint(x1, y1), true); var lineSegment2 = new LineSegment(new SysPoint(x2, y2), true); #else var lineSegment1 = new LineSegment { Point = new Point(x1, y1) }; var lineSegment2 = new LineSegment { Point = new Point(x2, y2) }; #endif figure.Segments.Add(lineSegment1); figure.Segments.Add(lineSegment2); } #endif } // ----- AddLines ----------------------------------------------------------------------------- #if GDI /// /// Adds a series of connected line segments to current figure. /// public void AddLines(System.Drawing.Point[] points) { AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if WPF /// /// Adds a series of connected line segments to current figure. /// public void AddLines(SysPoint[] points) { AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if GDI /// /// Adds a series of connected line segments to current figure. /// public void AddLines(PointF[] points) { AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif /// /// Adds a series of connected line segments to current figure. /// public void AddLines(XPoint[] points) { if (points == null) throw new ArgumentNullException("points"); int count = points.Length; if (count == 0) return; #if CORE _corePath.MoveOrLineTo(points[0].X, points[0].Y); for (int idx = 1; idx < count; idx++) _corePath.LineTo(points[idx].X, points[idx].Y, false); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddLines(XGraphics.MakePointFArray(points)); } finally { Lock.ExitGdiPlus(); } #endif #if WPF PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) { figure.StartPoint = new SysPoint(points[0].X, points[0].Y); for (int idx = 1; idx < count; idx++) { #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(new SysPoint(points[idx].X, points[idx].Y), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = new Point(points[idx].X, points[idx].Y); // ,true? #endif figure.Segments.Add(lineSegment); } } else { for (int idx = 0; idx < count; idx++) { // figure.Segments.Add(new LineSegment(new SysPoint(points[idx].x, points[idx].y), true)); #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(new SysPoint(points[idx].X, points[idx].Y), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = new Point(points[idx].X, points[idx].Y); // ,true? #endif figure.Segments.Add(lineSegment); } } #endif } // ----- AddBezier ---------------------------------------------------------------------------- #if GDI /// /// Adds a cubic Bézier curve to the current figure. /// public void AddBezier(System.Drawing.Point pt1, System.Drawing.Point pt2, System.Drawing.Point pt3, System.Drawing.Point pt4) { AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); } #endif #if WPF /// /// Adds a cubic Bézier curve to the current figure. /// public void AddBezier(SysPoint pt1, SysPoint pt2, SysPoint pt3, SysPoint pt4) { AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); } #endif #if GDI /// /// Adds a cubic Bézier curve to the current figure. /// public void AddBezier(PointF pt1, PointF pt2, PointF pt3, PointF pt4) { AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); } #endif /// /// Adds a cubic Bézier curve to the current figure. /// public void AddBezier(XPoint pt1, XPoint pt2, XPoint pt3, XPoint pt4) { AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); } /// /// Adds a cubic Bézier curve to the current figure. /// public void AddBezier(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { #if CORE _corePath.MoveOrLineTo(x1, y1); _corePath.BezierTo(x2, y2, x3, y3, x4, y4, false); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddBezier((float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3, (float)x4, (float)y4); } finally { Lock.ExitGdiPlus(); } #endif #if WPF PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) figure.StartPoint = new SysPoint(x1, y1); else { // figure.Segments.Add(new LineSegment(new SysPoint(x1, y1), true)); #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(new SysPoint(x1, y1), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = new Point(x1, y1); #endif figure.Segments.Add(lineSegment); } //figure.Segments.Add(new BezierSegment( // new SysPoint(x2, y2), // new SysPoint(x3, y3), // new SysPoint(x4, y4), true)); #if !SILVERLIGHT BezierSegment bezierSegment = new BezierSegment( new SysPoint(x2, y2), new SysPoint(x3, y3), new SysPoint(x4, y4), true); #else BezierSegment bezierSegment = new BezierSegment(); bezierSegment.Point1 = new Point(x2, y2); bezierSegment.Point2 = new Point(x3, y3); bezierSegment.Point3 = new Point(x4, y4); #endif figure.Segments.Add(bezierSegment); #endif } // ----- AddBeziers --------------------------------------------------------------------------- #if GDI /// /// Adds a sequence of connected cubic Bézier curves to the current figure. /// public void AddBeziers(System.Drawing.Point[] points) { AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if WPF /// /// Adds a sequence of connected cubic Bézier curves to the current figure. /// public void AddBeziers(SysPoint[] points) { AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if GDI /// /// Adds a sequence of connected cubic Bézier curves to the current figure. /// public void AddBeziers(PointF[] points) { AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif /// /// Adds a sequence of connected cubic Bézier curves to the current figure. /// public void AddBeziers(XPoint[] points) { if (points == null) throw new ArgumentNullException("points"); int count = points.Length; if (count < 4) throw new ArgumentException("At least four points required for bezier curve.", "points"); if ((count - 1) % 3 != 0) throw new ArgumentException("Invalid number of points for bezier curve. Number must fulfil 4+3n.", "points"); #if CORE _corePath.MoveOrLineTo(points[0].X, points[0].Y); for (int idx = 1; idx < count; idx += 3) { _corePath.BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, points[idx + 2].X, points[idx + 2].Y, false); } #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddBeziers(XGraphics.MakePointFArray(points)); } finally { Lock.ExitGdiPlus(); } #endif #if WPF PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) figure.StartPoint = new SysPoint(points[0].X, points[0].Y); else { // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(new SysPoint(points[0].X, points[0].Y), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = new Point(points[0].X, points[0].Y); #endif figure.Segments.Add(lineSegment); } for (int idx = 1; idx < count; idx += 3) { //figure.Segments.Add(new BezierSegment( // new SysPoint(points[idx].x, points[idx].y), // new SysPoint(points[idx + 1].x, points[idx + 1].y), // new SysPoint(points[idx + 2].x, points[idx + 2].y), true)); #if !SILVERLIGHT BezierSegment bezierSegment = new BezierSegment( new SysPoint(points[idx].X, points[idx].Y), new SysPoint(points[idx + 1].X, points[idx + 1].Y), new SysPoint(points[idx + 2].X, points[idx + 2].Y), true); #else BezierSegment bezierSegment = new BezierSegment(); bezierSegment.Point1 = new Point(points[idx].X, points[idx].Y); bezierSegment.Point2 = new Point(points[idx + 1].X, points[idx + 1].Y); bezierSegment.Point3 = new Point(points[idx + 2].X, points[idx + 2].Y); #endif figure.Segments.Add(bezierSegment); } #endif } // ----- AddCurve ----------------------------------------------------------------------- #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(System.Drawing.Point[] points) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if WPF /// /// Adds a spline curve to the current figure. /// public void AddCurve(SysPoint[] points) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(PointF[] points) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); } #endif /// /// Adds a spline curve to the current figure. /// public void AddCurve(XPoint[] points) { AddCurve(points, 0.5); } #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(System.Drawing.Point[] points, double tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif #if WPF /// /// Adds a spline curve to the current figure. /// public void AddCurve(SysPoint[] points, double tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(PointF[] points, double tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif /// /// Adds a spline curve to the current figure. /// public void AddCurve(XPoint[] points, double tension) { int count = points.Length; if (count < 2) throw new ArgumentException("AddCurve requires two or more points.", "points"); #if CORE _corePath.AddCurve(points, tension); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddCurve(XGraphics.MakePointFArray(points), (float)tension); } finally { Lock.ExitGdiPlus(); } #endif #if WPF tension /= 3; PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) figure.StartPoint = new SysPoint(points[0].X, points[0].Y); else { // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(new SysPoint(points[0].X, points[0].Y), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = new Point(points[0].X, points[0].Y); #endif figure.Segments.Add(lineSegment); } if (count == 2) { figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); } else { figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension)); for (int idx = 1; idx < count - 2; idx++) figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension)); } #endif } #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(System.Drawing.Point[] points, int offset, int numberOfSegments, float tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); } #endif #if WPF /// /// Adds a spline curve to the current figure. /// public void AddCurve(SysPoint[] points, int offset, int numberOfSegments, float tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); } #endif #if GDI /// /// Adds a spline curve to the current figure. /// public void AddCurve(PointF[] points, int offset, int numberOfSegments, float tension) { AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); } #endif /// /// Adds a spline curve to the current figure. /// public void AddCurve(XPoint[] points, int offset, int numberOfSegments, double tension) { #if CORE throw new NotImplementedException("AddCurve not yet implemented."); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddCurve(XGraphics.MakePointFArray(points), offset, numberOfSegments, (float)tension); } finally { Lock.ExitGdiPlus(); } #endif #if WPF throw new NotImplementedException("AddCurve not yet implemented."); #endif } // ----- AddArc ------------------------------------------------------------------------------- #if GDI /// /// Adds an elliptical arc to the current figure. /// public void AddArc(Rectangle rect, double startAngle, double sweepAngle) { AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); } #endif #if GDI /// /// Adds an elliptical arc to the current figure. /// public void AddArc(RectangleF rect, double startAngle, double sweepAngle) { AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); } #endif /// /// Adds an elliptical arc to the current figure. /// public void AddArc(XRect rect, double startAngle, double sweepAngle) { AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); } /// /// Adds an elliptical arc to the current figure. /// public void AddArc(double x, double y, double width, double height, double startAngle, double sweepAngle) { #if CORE _corePath.AddArc(x, y, width, height, startAngle, sweepAngle); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddArc((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); } finally { Lock.ExitGdiPlus(); } #endif #if WPF PathFigure figure = CurrentPathFigure; SysPoint startPoint; ArcSegment seg = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startPoint); if (figure.Segments.Count == 0) figure.StartPoint = startPoint; else { //#if !SILVERLIGHT // LineSegment lineSegment = new LineSegment(startPoint, true); //#else LineSegment lineSegment = new LineSegment(); lineSegment.Point = startPoint; //#endif figure.Segments.Add(lineSegment); } figure.Segments.Add(seg); //figure.Segments.Add( //if (figure.Segments.Count == 0) // figure.StartPoint = new SysPoint(points[0].x, points[0].y); //else // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); //for (int idx = 1; idx < 5555; idx += 3) // figure.Segments.Add(new BezierSegment( // new SysPoint(points[idx].x, points[idx].y), // new SysPoint(points[idx + 1].x, points[idx + 1].y), // new SysPoint(points[idx + 2].x, points[idx + 2].y), true)); #endif } /// /// Adds an elliptical arc to the current figure. The arc is specified WPF like. /// public void AddArc(XPoint point1, XPoint point2, XSize size, double rotationAngle, bool isLargeArg, XSweepDirection sweepDirection) { #if CORE _corePath.AddArc(point1, point2, size, rotationAngle, isLargeArg, sweepDirection); #endif #if GDI DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddArc"); #endif #if WPF PathFigure figure = CurrentPathFigure; if (figure.Segments.Count == 0) figure.StartPoint = point1.ToPoint(); else { // figure.Segments.Add(new LineSegment(point1.ToPoint(), true)); #if !SILVERLIGHT LineSegment lineSegment = new LineSegment(point1.ToPoint(), true); #else LineSegment lineSegment = new LineSegment(); lineSegment.Point = point1.ToPoint(); #endif figure.Segments.Add(lineSegment); } // figure.Segments.Add(new ArcSegment(point2.ToPoint(), size.ToSize(), rotationAngle, isLargeArg, sweepDirection, true)); #if !SILVERLIGHT ArcSegment arcSegment = new ArcSegment(point2.ToPoint(), size.ToSize(), rotationAngle, isLargeArg, (SweepDirection)sweepDirection, true); #else ArcSegment arcSegment = new ArcSegment(); arcSegment.Point = point2.ToPoint(); arcSegment.Size = size.ToSize(); arcSegment.RotationAngle = rotationAngle; arcSegment.IsLargeArc = isLargeArg; arcSegment.SweepDirection = (SweepDirection)sweepDirection; #endif figure.Segments.Add(arcSegment); #endif } // ----- AddRectangle ------------------------------------------------------------------------- #if GDI /// /// Adds a rectangle to this path. /// public void AddRectangle(Rectangle rect) { AddRectangle(new XRect(rect)); } #endif #if GDI /// /// Adds a rectangle to this path. /// public void AddRectangle(RectangleF rect) { AddRectangle(new XRect(rect)); } #endif /// /// Adds a rectangle to this path. /// public void AddRectangle(XRect rect) { #if CORE _corePath.MoveTo(rect.X, rect.Y); _corePath.LineTo(rect.X + rect.Width, rect.Y, false); _corePath.LineTo(rect.X + rect.Width, rect.Y + rect.Height, false); _corePath.LineTo(rect.X, rect.Y + rect.Height, true); _corePath.CloseSubpath(); #endif #if GDI try { Lock.EnterGdiPlus(); // If rect is empty GDI+ removes the rect from the path. // This is not intended if the path is used for clipping. // See http://forum.pdfsharp.net/viewtopic.php?p=9433#p9433 // _gdipPath.AddRectangle(rect.ToRectangleF()); // Draw the rectangle manually. _gdipPath.StartFigure(); _gdipPath.AddLines(new PointF[] { rect.TopLeft.ToPointF(), rect.TopRight.ToPointF(), rect.BottomRight.ToPointF(), rect.BottomLeft.ToPointF() }); _gdipPath.CloseFigure(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF StartFigure(); PathFigure figure = CurrentPathFigure; figure.StartPoint = new SysPoint(rect.X, rect.Y); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y), true)); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y + rect.height), true)); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x, rect.y + rect.height), true)); #if !SILVERLIGHT LineSegment lineSegment1 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y), true); LineSegment lineSegment2 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y + rect.Height), true); LineSegment lineSegment3 = new LineSegment(new SysPoint(rect.X, rect.Y + rect.Height), true); #else LineSegment lineSegment1 = new LineSegment(); lineSegment1.Point = new Point(rect.X + rect.Width, rect.Y); LineSegment lineSegment2 = new LineSegment(); lineSegment2.Point = new Point(rect.X + rect.Width, rect.Y + rect.Height); LineSegment lineSegment3 = new LineSegment(); lineSegment3.Point = new Point(rect.X, rect.Y + rect.Height); #endif figure.Segments.Add(lineSegment1); figure.Segments.Add(lineSegment2); figure.Segments.Add(lineSegment3); CloseFigure(); #endif } /// /// Adds a rectangle to this path. /// public void AddRectangle(double x, double y, double width, double height) { AddRectangle(new XRect(x, y, width, height)); } // ----- AddRectangles ------------------------------------------------------------------------ #if GDI /// /// Adds a series of rectangles to this path. /// public void AddRectangles(Rectangle[] rects) { int count = rects.Length; for (int idx = 0; idx < count; idx++) AddRectangle(rects[idx]); try { Lock.EnterGdiPlus(); _gdipPath.AddRectangles(rects); } finally { Lock.ExitGdiPlus(); } } #endif #if GDI /// /// Adds a series of rectangles to this path. /// public void AddRectangles(RectangleF[] rects) { int count = rects.Length; for (int idx = 0; idx < count; idx++) AddRectangle(rects[idx]); try { Lock.EnterGdiPlus(); _gdipPath.AddRectangles(rects); } finally { Lock.ExitGdiPlus(); } } #endif /// /// Adds a series of rectangles to this path. /// public void AddRectangles(XRect[] rects) { int count = rects.Length; for (int idx = 0; idx < count; idx++) { #if CORE AddRectangle(rects[idx]); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddRectangle(rects[idx].ToRectangleF()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF StartFigure(); PathFigure figure = CurrentPathFigure; XRect rect = rects[idx]; figure.StartPoint = new SysPoint(rect.X, rect.Y); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y), true)); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y + rect.height), true)); // figure.Segments.Add(new LineSegment(new SysPoint(rect.x, rect.y + rect.height), true)); #if !SILVERLIGHT LineSegment lineSegment1 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y), true); LineSegment lineSegment2 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y + rect.Height), true); LineSegment lineSegment3 = new LineSegment(new SysPoint(rect.X, rect.Y + rect.Height), true); #else LineSegment lineSegment1 = new LineSegment(); lineSegment1.Point = new Point(rect.X + rect.Width, rect.Y); LineSegment lineSegment2 = new LineSegment(); lineSegment2.Point = new Point(rect.X + rect.Width, rect.Y + rect.Height); LineSegment lineSegment3 = new LineSegment(); lineSegment3.Point = new Point(rect.X, rect.Y + rect.Height); #endif figure.Segments.Add(lineSegment1); figure.Segments.Add(lineSegment2); figure.Segments.Add(lineSegment3); CloseFigure(); #endif } } // ----- AddRoundedRectangle ------------------------------------------------------------------ #if GDI /// /// Adds a rectangle with rounded corners to this path. /// public void AddRoundedRectangle(Rectangle rect, System.Drawing.Size ellipseSize) { AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); } #endif #if WPF || NETFX_CORE /// /// Adds a rectangle with rounded corners to this path. /// public void AddRoundedRectangle(SysRect rect, SysSize ellipseSize) { AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); } #endif #if GDI /// /// Adds a rectangle with rounded corners to this path. /// public void AddRoundedRectangle(RectangleF rect, SizeF ellipseSize) { AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); } #endif #if GDI /// /// Adds a rectangle with rounded corners to this path. /// public void AddRoundedRectangle(XRect rect, SizeF ellipseSize) { AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); } #endif /// /// Adds a rectangle with rounded corners to this path. /// public void AddRoundedRectangle(double x, double y, double width, double height, double ellipseWidth, double ellipseHeight) { #if CORE #if true double arcWidth = ellipseWidth / 2; double arcHeight = ellipseHeight / 2; #if true // Clockwise _corePath.MoveTo(x + width - arcWidth, y); _corePath.QuadrantArcTo(x + width - arcWidth, y + arcHeight, arcWidth, arcHeight, 1, true); _corePath.LineTo(x + width, y + height - arcHeight, false); _corePath.QuadrantArcTo(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight, 4, true); _corePath.LineTo(x + arcWidth, y + height, false); _corePath.QuadrantArcTo(x + arcWidth, y + height - arcHeight, arcWidth, arcHeight, 3, true); _corePath.LineTo(x, y + arcHeight, false); _corePath.QuadrantArcTo(x + arcWidth, y + arcHeight, arcWidth, arcHeight, 2, true); _corePath.CloseSubpath(); #else // Counterclockwise _corePath.MoveTo(x + arcWidth, y); _corePath.QuadrantArcTo(x + arcWidth, y + arcHeight, arcWidth, arcHeight, 2, false); _corePath.LineTo(x, y + height - arcHeight, false); _corePath.QuadrantArcTo(x + arcWidth, y + height - arcHeight, arcWidth, arcHeight, 3, false); _corePath.LineTo(x + width - arcWidth, y + height, false); _corePath.QuadrantArcTo(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight, 4, false); _corePath.LineTo(x + width, y + arcHeight, false); _corePath.QuadrantArcTo(x + width - arcWidth, y + arcHeight, arcWidth, arcHeight, 1, false); _corePath.CloseSubpath(); #endif #else // AddArc not yet implemented AddArc((float)(x + width - ellipseWidth), (float)y, (float)ellipseWidth, (float)ellipseHeight, -90, 90); AddArc((float)(x + width - ellipseWidth), (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 0, 90); AddArc((float)x, (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 90, 90); AddArc((float)x, (float)y, (float)ellipseWidth, (float)ellipseHeight, 180, 90); CloseFigure(); #endif #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.StartFigure(); _gdipPath.AddArc((float)(x + width - ellipseWidth), (float)y, (float)ellipseWidth, (float)ellipseHeight, -90, 90); _gdipPath.AddArc((float)(x + width - ellipseWidth), (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 0, 90); _gdipPath.AddArc((float)x, (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 90, 90); _gdipPath.AddArc((float)x, (float)y, (float)ellipseWidth, (float)ellipseHeight, 180, 90); _gdipPath.CloseFigure(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE double ex = ellipseWidth / 2; double ey = ellipseHeight / 2; StartFigure(); PathFigure figure = CurrentPathFigure; figure.StartPoint = new SysPoint(x + ex, y); //#if !SILVERLIGHT // figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y), true)); // // TODOWPF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx // figure.Segments.Add(new ArcSegment(new SysPoint(x + width, y + ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); // //figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + ey), true)); // figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + height - ey), true)); // // TODOWPF // figure.Segments.Add(new ArcSegment(new SysPoint(x + width - ex, y + height), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); // //figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y + height), true)); // figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y + height), true)); // // TODOWPF // figure.Segments.Add(new ArcSegment(new SysPoint(x, y + height - ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); // //figure.Segments.Add(new LineSegment(new SysPoint(x, y + height - ey), true)); // figure.Segments.Add(new LineSegment(new SysPoint(x, y + ey), true)); // // TODOWPF // figure.Segments.Add(new ArcSegment(new SysPoint(x + ex, y), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); // //figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y), true)); //#else #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y), true)); #else figure.Segments.Add(new LineSegment { Point = new SysPoint(x + width - ex, y) }); #endif // TODOWPF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new ArcSegment(new SysPoint(x + width, y + ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); //figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + ey), true)); #else figure.Segments.Add(new ArcSegment { Point = new SysPoint(x + width, y + ey), Size = new SysSize(ex, ey), //RotationAngle = 0, //IsLargeArc = false, SweepDirection = SweepDirection.Clockwise }); #endif #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + height - ey), true)); #else figure.Segments.Add(new LineSegment { Point = new SysPoint(x + width, y + height - ey) }); #endif // TODOWPF #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new ArcSegment(new SysPoint(x + width - ex, y + height), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); //figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y + height), true)); #else figure.Segments.Add(new ArcSegment { Point = new SysPoint(x + width - ex, y + height), Size = new SysSize(ex, ey), //RotationAngle = 0, //IsLargeArc = false, SweepDirection = SweepDirection.Clockwise }); #endif #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y + height), true)); #else figure.Segments.Add(new LineSegment { Point = new SysPoint(x + ex, y + height) }); #endif // TODOWPF #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new ArcSegment(new SysPoint(x, y + height - ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); //figure.Segments.Add(new LineSegment(new SysPoint(x, y + height - ey), true)); #else figure.Segments.Add(new ArcSegment { Point = new SysPoint(x, y + height - ey), Size = new SysSize(ex, ey), //RotationAngle = 0, //IsLargeArc = false, SweepDirection = SweepDirection.Clockwise }); #endif #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new LineSegment(new SysPoint(x, y + ey), true)); #else figure.Segments.Add(new LineSegment { Point = new SysPoint(x, y + ey) }); #endif // TODOWPF #if !SILVERLIGHT && !NETFX_CORE figure.Segments.Add(new ArcSegment(new SysPoint(x + ex, y), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); //figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y), true)); #else figure.Segments.Add(new ArcSegment { Point = new SysPoint(x + ex, y), Size = new SysSize(ex, ey), //RotationAngle = 0, //IsLargeArc = false, SweepDirection = SweepDirection.Clockwise }); #endif CloseFigure(); #endif } // ----- AddEllipse --------------------------------------------------------------------------- #if GDI /// /// Adds an ellipse to the current path. /// public void AddEllipse(Rectangle rect) { AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); } #endif #if GDI /// /// Adds an ellipse to the current path. /// public void AddEllipse(RectangleF rect) { AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); } #endif /// /// Adds an ellipse to the current path. /// public void AddEllipse(XRect rect) { AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); } /// /// Adds an ellipse to the current path. /// public void AddEllipse(double x, double y, double width, double height) { #if CORE double w = width / 2; double h = height / 2; double xc = x + w; double yc = y + h; _corePath.MoveTo(x + w, y); _corePath.QuadrantArcTo(xc, yc, w, h, 1, true); _corePath.QuadrantArcTo(xc, yc, w, h, 4, true); _corePath.QuadrantArcTo(xc, yc, w, h, 3, true); _corePath.QuadrantArcTo(xc, yc, w, h, 2, true); _corePath.CloseSubpath(); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddEllipse((float)x, (float)y, (float)width, (float)height); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry.AddGeometry(new EllipseGeometry(new Rect(x, y, width, height))); #else var figure = new PathFigure(); figure.StartPoint = new SysPoint(x, y + height / 2); var segment = new ArcSegment { Point = new SysPoint(x + width, y + height / 2), Size = new SysSize(width / 2, height / 2), IsLargeArc = true, RotationAngle = 180, SweepDirection = SweepDirection.Clockwise, }; figure.Segments.Add(segment); segment = new ArcSegment { Point = figure.StartPoint, Size = new SysSize(width / 2, height / 2), IsLargeArc = true, RotationAngle = 180, SweepDirection = SweepDirection.Clockwise, }; figure.Segments.Add(segment); _pathGeometry.Figures.Add(figure); #endif // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, // but CloseFigure() is needed for the next adding not to continue this figure. CloseFigure(); #endif } // ----- AddPolygon --------------------------------------------------------------------------- #if GDI /// /// Adds a polygon to this path. /// public void AddPolygon(System.Drawing.Point[] points) { try { Lock.EnterGdiPlus(); _gdipPath.AddPolygon(points); } finally { Lock.ExitGdiPlus(); } } #endif #if WPF || NETFX_CORE /// /// Adds a polygon to this path. /// public void AddPolygon(SysPoint[] points) { // TODO: fill mode unclear here #if !SILVERLIGHT && !NETFX_CORE _pathGeometry.AddGeometry(GeometryHelper.CreatePolygonGeometry(points, XFillMode.Alternate, true)); CloseFigure(); // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, but CloseFigure() is needed for the next adding not to continue this figure. #else AddPolygon(XGraphics.MakeXPointArray(points, 0, points.Length)); #endif } #endif #if GDI /// /// Adds a polygon to this path. /// public void AddPolygon(PointF[] points) { try { Lock.EnterGdiPlus(); _gdipPath.AddPolygon(points); } finally { Lock.ExitGdiPlus(); } } #endif /// /// Adds a polygon to this path. /// public void AddPolygon(XPoint[] points) { #if CORE int count = points.Length; if (count == 0) return; _corePath.MoveTo(points[0].X, points[0].Y); for (int idx = 0; idx < count - 1; idx++) _corePath.LineTo(points[idx].X, points[idx].Y, false); _corePath.LineTo(points[count - 1].X, points[count - 1].Y, true); _corePath.CloseSubpath(); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddPolygon(XGraphics.MakePointFArray(points)); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry.AddGeometry(GeometryHelper.CreatePolygonGeometry(XGraphics.MakePointArray(points), XFillMode.Alternate, true)); #else var figure = new PathFigure(); figure.StartPoint = new SysPoint(points[0].X, points[0].Y); figure.IsClosed = true; PolyLineSegment segment = new PolyLineSegment(); int count = points.Length; // For correct drawing the start point of the segment must not be the same as the first point. for (int idx = 1; idx < count; idx++) segment.Points.Add(new SysPoint(points[idx].X, points[idx].Y)); #if !SILVERLIGHT && !NETFX_CORE seg.IsStroked = true; #endif figure.Segments.Add(segment); _pathGeometry.Figures.Add(figure); #endif // TODO: NOT NEEDED //CloseFigure(); // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, but CloseFigure() is needed for the next adding not to continue this figure. #endif } // ----- AddPie ------------------------------------------------------------------------------- #if GDI /// /// Adds the outline of a pie shape to this path. /// public void AddPie(Rectangle rect, double startAngle, double sweepAngle) { try { Lock.EnterGdiPlus(); _gdipPath.AddPie(rect, (float)startAngle, (float)sweepAngle); } finally { Lock.ExitGdiPlus(); } } #endif #if GDI /// /// Adds the outline of a pie shape to this path. /// public void AddPie(RectangleF rect, double startAngle, double sweepAngle) { AddPie(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); } #endif /// /// Adds the outline of a pie shape to this path. /// public void AddPie(XRect rect, double startAngle, double sweepAngle) { AddPie(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); } /// /// Adds the outline of a pie shape to this path. /// public void AddPie(double x, double y, double width, double height, double startAngle, double sweepAngle) { #if CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPie"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddPie((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPie"); #endif } // ----- AddClosedCurve ------------------------------------------------------------------------ #if GDI /// /// Adds a closed curve to this path. /// public void AddClosedCurve(System.Drawing.Point[] points) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); } #endif #if WPF || NETFX_CORE /// /// Adds a closed curve to this path. /// public void AddClosedCurve(SysPoint[] points) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); } #endif #if GDI /// /// Adds a closed curve to this path. /// public void AddClosedCurve(PointF[] points) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); } #endif /// /// Adds a closed curve to this path. /// public void AddClosedCurve(XPoint[] points) { AddClosedCurve(points, 0.5); } #if GDI /// /// Adds a closed curve to this path. /// public void AddClosedCurve(System.Drawing.Point[] points, double tension) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif #if WPF || NETFX_CORE /// /// Adds a closed curve to this path. /// public void AddClosedCurve(SysPoint[] points, double tension) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif #if GDI /// /// Adds a closed curve to this path. /// public void AddClosedCurve(PointF[] points, double tension) { AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); } #endif /// /// Adds a closed curve to this path. /// public void AddClosedCurve(XPoint[] points, double tension) { if (points == null) throw new ArgumentNullException("points"); int count = points.Length; if (count == 0) return; if (count < 2) throw new ArgumentException("Not enough points.", "points"); #if CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddClosedCurve"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddClosedCurve(XGraphics.MakePointFArray(points), (float)tension); } finally { Lock.ExitGdiPlus(); } #endif #if WPF ||NETFX_CORE tension /= 3; StartFigure(); PathFigure figure = CurrentPathFigure; figure.StartPoint = new SysPoint(points[0].X, points[0].Y); if (count == 2) { figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); } else { figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 1], points[0], points[1], points[2], tension)); for (int idx = 1; idx < count - 2; idx++) figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[0], tension)); figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 2], points[count - 1], points[0], points[1], tension)); } #endif } // ----- AddPath ------------------------------------------------------------------------------ /// /// Adds the specified path to this path. /// public void AddPath(XGraphicsPath path, bool connect) { #if CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPath"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.AddPath(path._gdipPath, connect); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry.AddGeometry(path._pathGeometry); #else // AG-HACK: No AddGeometry in Silverlight version of PathGeometry throw new InvalidOperationException("Silverlight/WinRT cannot merge geometry objects."); // TODO: make it manually by using a GeometryGroup #endif #endif } // ----- AddString ---------------------------------------------------------------------------- #if GDI /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, System.Drawing.Point origin, XStringFormat format) { AddString(s, family, style, emSize, new XRect(origin.X, origin.Y, 0, 0), format); } #endif #if WPF || NETFX_CORE /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, SysPoint origin, XStringFormat format) { AddString(s, family, style, emSize, new XPoint(origin), format); } #endif #if GDI /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, PointF origin, XStringFormat format) { AddString(s, family, style, emSize, new XRect(origin.X, origin.Y, 0, 0), format); } #endif /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, XPoint origin, XStringFormat format) { try { #if CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddString"); #endif #if GDI if (family.GdiFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); PointF p = origin.ToPointF(); p.Y += SimulateBaselineOffset(family, style, emSize, format); try { Lock.EnterGdiPlus(); _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, p, format.RealizeGdiStringFormat()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF if (family.WpfFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); #if !SILVERLIGHT XFont font = new XFont(family.Name, emSize, style); double x = origin.X; double y = origin.Y; double lineSpace = font.GetHeight(); double cyAscent = lineSpace * font.CellAscent / font.CellSpace; double cyDescent = lineSpace * font.CellDescent / font.CellSpace; Typeface typeface = FontHelper.CreateTypeface(family.WpfFamily, style); FormattedText formattedText = FontHelper.CreateFormattedText(s, typeface, emSize, WpfBrushes.Black); switch (format.Alignment) { case XStringAlignment.Near: // nothing to do, this is the default //formattedText.TextAlignment = TextAlignment.Left; break; case XStringAlignment.Center: formattedText.TextAlignment = TextAlignment.Center; break; case XStringAlignment.Far: formattedText.TextAlignment = TextAlignment.Right; break; } switch (format.LineAlignment) { case XLineAlignment.Near: //y += cyAscent; break; case XLineAlignment.Center: // TODO use CapHeight. PDFlib also uses 3/4 of ascent y += -lineSpace / 2; //-formattedText.Baseline + (cyAscent * 2 / 4); break; case XLineAlignment.Far: y += -formattedText.Baseline - cyDescent; break; case XLineAlignment.BaseLine: y -= formattedText.Baseline; break; } Geometry geo = formattedText.BuildGeometry(new XPoint(x, y)); _pathGeometry.AddGeometry(geo); #else // AG-HACK throw new InvalidOperationException("Silverlight cannot create geometry of glyphs."); // TODO: Get the outline directly from the font. #endif #endif } catch { throw; } } #if GDI /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, Rectangle layoutRect, XStringFormat format) { if (family.GdiFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); Rectangle rect = new Rectangle(layoutRect.X, layoutRect.Y, layoutRect.Width, layoutRect.Height); rect.Offset(new System.Drawing.Point(0, (int)SimulateBaselineOffset(family, style, emSize, format))); try { Lock.EnterGdiPlus(); _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, rect, format.RealizeGdiStringFormat()); } finally { Lock.ExitGdiPlus(); } } /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, RectangleF layoutRect, XStringFormat format) { if (family.GdiFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); RectangleF rect = new RectangleF(layoutRect.X, layoutRect.Y, layoutRect.Width, layoutRect.Height); rect.Offset(new PointF(0, SimulateBaselineOffset(family, style, emSize, format))); try { Lock.EnterGdiPlus(); _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, layoutRect, format.RealizeGdiStringFormat()); } finally { Lock.ExitGdiPlus(); } } /// /// Calculates the offset for BaseLine positioning simulation: /// In GDI we have only Near, Center and Far as LineAlignment and no BaseLine. For XLineAlignment.BaseLine StringAlignment.Near is returned. /// We now return the negative drawed ascender height. /// This has to be added to the LayoutRect/Origin before each _gdipPath.AddString(). /// /// /// /// /// /// private float SimulateBaselineOffset(XFontFamily family, XFontStyle style, double emSize, XStringFormat format) { XFont font = new XFont(family.Name, emSize, style); if (format.LineAlignment == XLineAlignment.BaseLine) { double lineSpace = font.GetHeight(); int cellSpace = font.FontFamily.GetLineSpacing(font.Style); int cellAscent = font.FontFamily.GetCellAscent(font.Style); int cellDescent = font.FontFamily.GetCellDescent(font.Style); double cyAscent = lineSpace * cellAscent / cellSpace; cyAscent = lineSpace * font.CellAscent / font.CellSpace; return (float)-cyAscent; } return 0; } #endif #if WPF /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, Rect rect, XStringFormat format) { //gdip Path.AddString(s, family.gdiFamily, (int)style, (float)emSize, layoutRect, format.RealizeGdiStringFormat()); AddString(s, family, style, emSize, new XRect(rect), format); } #endif /// /// Adds a text string to this path. /// public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, XRect layoutRect, XStringFormat format) { if (s == null) throw new ArgumentNullException("s"); if (family == null) throw new ArgumentNullException("family"); if (format == null) format = XStringFormats.Default; if (format.LineAlignment == XLineAlignment.BaseLine && layoutRect.Height != 0) throw new InvalidOperationException( "DrawString: With XLineAlignment.BaseLine the height of the layout rectangle must be 0."); if (s.Length == 0) return; XFont font = new XFont(family.Name, emSize, style); #if CORE DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddString"); #endif #if (GDI || CORE_) && !WPF //Gfx.DrawString(text, font.Realize_GdiFont(), brush.RealizeGdiBrush(), rect, // format != null ? format.RealizeGdiStringFormat() : null); if (family.GdiFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); RectangleF rect = layoutRect.ToRectangleF(); rect.Offset(new PointF(0, SimulateBaselineOffset(family, style, emSize, format))); try { Lock.EnterGdiPlus(); _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, rect, format.RealizeGdiStringFormat()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF && !GDI if (family.WpfFamily == null) throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); #if !SILVERLIGHT // Just a first sketch, but currently we do not need it and there is enough to do... double x = layoutRect.X; double y = layoutRect.Y; //double lineSpace = font.GetHeight(this); //double cyAscent = lineSpace * font.cellAscent / font.cellSpace; //double cyDescent = lineSpace * font.cellDescent / font.cellSpace; //double cyAscent = family.GetCellAscent(style) * family.GetLineSpacing(style) / family.getl; //fontlineSpace * font.cellAscent / font.cellSpace; //double cyDescent =family.GetCellDescent(style); // lineSpace * font.cellDescent / font.cellSpace; double lineSpace = font.GetHeight(); double cyAscent = lineSpace * font.CellAscent / font.CellSpace; double cyDescent = lineSpace * font.CellDescent / font.CellSpace; bool bold = (style & XFontStyle.Bold) != 0; bool italic = (style & XFontStyle.Italic) != 0; bool strikeout = (style & XFontStyle.Strikeout) != 0; bool underline = (style & XFontStyle.Underline) != 0; Typeface typeface = FontHelper.CreateTypeface(family.WpfFamily, style); //FormattedText formattedText = new FormattedText(s, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, emSize, WpfBrushes.Black); FormattedText formattedText = FontHelper.CreateFormattedText(s, typeface, emSize, WpfBrushes.Black); switch (format.Alignment) { case XStringAlignment.Near: // nothing to do, this is the default //formattedText.TextAlignment = TextAlignment.Left; break; case XStringAlignment.Center: x += layoutRect.Width / 2; formattedText.TextAlignment = TextAlignment.Center; break; case XStringAlignment.Far: x += layoutRect.Width; formattedText.TextAlignment = TextAlignment.Right; break; } //if (PageDirection == XPageDirection.Downwards) //{ switch (format.LineAlignment) { case XLineAlignment.Near: //y += cyAscent; break; case XLineAlignment.Center: // TO/DO use CapHeight. PDFlib also uses 3/4 of ascent //y += -formattedText.Baseline + (cyAscent * 2 / 4) + layoutRect.Height / 2; // GDI seems to make it this simple: // TODO: Check WPF's vertical alignment and make all implementations fit. $MaOs y += layoutRect.Height / 2 - lineSpace / 2; break; case XLineAlignment.Far: y += -formattedText.Baseline - cyDescent + layoutRect.Height; break; case XLineAlignment.BaseLine: y -= formattedText.Baseline; break; } //} //else //{ // // TODOWPF // switch (format.LineAlignment) // { // case XLineAlignment.Near: // //y += cyDescent; // break; // case XLineAlignment.Center: // // TODO use CapHeight. PDFlib also uses 3/4 of ascent // //y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2; // break; // case XLineAlignment.Far: // //y += -cyAscent + rect.Height; // break; // case XLineAlignment.BaseLine: // // nothing to do // break; // } //} //if (bold && !descriptor.IsBoldFace) //{ // // TODO: emulate bold by thicker outline //} //if (italic && !descriptor.IsItalicFace) //{ // // TODO: emulate italic by shearing transformation //} if (underline) { //double underlinePosition = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlinePosition / font.cellSpace; //double underlineThickness = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlineThickness / font.cellSpace; //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness); } if (strikeout) { //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace; //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace; //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); } //dc.DrawText(formattedText, layoutRectangle.Location.ToPoint()); //dc.DrawText(formattedText, new SysPoint(x, y)); Geometry geo = formattedText.BuildGeometry(new Point(x, y)); _pathGeometry.AddGeometry(geo); #else // AG-HACK throw new InvalidOperationException("Silverlight cannot create geometry of glyphs."); // TODO: no, yagni #endif #endif } // -------------------------------------------------------------------------------------------- /// /// Closes the current figure and starts a new figure. /// public void CloseFigure() { #if CORE _corePath.CloseSubpath(); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.CloseFigure(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE PathFigure figure = PeekCurrentFigure; if (figure != null && figure.Segments.Count != 0) figure.IsClosed = true; #endif } /// /// Starts a new figure without closing the current figure. /// public void StartFigure() { #if CORE // TODO: ??? #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.StartFigure(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE PathFigure figure = CurrentPathFigure; if (figure.Segments.Count != 0) { figure = new PathFigure(); _pathGeometry.Figures.Add(figure); } #endif } // -------------------------------------------------------------------------------------------- /// /// Gets or sets an XFillMode that determines how the interiors of shapes are filled. /// public XFillMode FillMode { get { return _fillMode; } set { _fillMode = value; #if CORE // Nothing to do. #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.FillMode = (FillMode)value; } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE _pathGeometry.FillRule = value == XFillMode.Winding ? FillRule.Nonzero : FillRule.EvenOdd; #endif } } private XFillMode _fillMode; // -------------------------------------------------------------------------------------------- /// /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. /// public void Flatten() { #if CORE // Just do nothing. #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Flatten(); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); #else // AGHACK throw new InvalidOperationException("Silverlight/WinrRT cannot flatten a geometry."); // TODO: no, yagni #endif #endif } /// /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. /// public void Flatten(XMatrix matrix) { #if CORE // Just do nothing. #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Flatten(matrix.ToGdiMatrix()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); _pathGeometry.Transform = new MatrixTransform(matrix.ToWpfMatrix()); #else // AGHACK throw new InvalidOperationException("Silverlight/WinRT cannot flatten a geometry."); // TODO: no, yagni #endif #endif } /// /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. /// public void Flatten(XMatrix matrix, double flatness) { #if CORE // Just do nothing. #endif #if CORE___ throw new NotImplementedException("XGraphicsPath.Flatten"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Flatten(matrix.ToGdiMatrix(), (float)flatness); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); // TODO: matrix handling not yet tested if (!matrix.IsIdentity) _pathGeometry.Transform = new MatrixTransform(matrix.ToWpfMatrix()); #else // AG-HACK throw new InvalidOperationException("Silverlight/WinRT cannot flatten a geometry."); // TODO: no, yagni #endif #endif } // -------------------------------------------------------------------------------------------- /// /// Replaces this path with curves that enclose the area that is filled when this path is drawn /// by the specified pen. /// public void Widen(XPen pen) { #if CORE // Just do nothing. #endif #if CORE___ throw new NotImplementedException("XGraphicsPath.Widen"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Widen(pen.RealizeGdiPen()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); #else // AG-HACK throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); // TODO: no, yagni #endif #endif } /// /// Replaces this path with curves that enclose the area that is filled when this path is drawn /// by the specified pen. /// public void Widen(XPen pen, XMatrix matrix) { #if CORE // Just do nothing. #endif #if CORE throw new NotImplementedException("XGraphicsPath.Widen"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Widen(pen.RealizeGdiPen(), matrix.ToGdiMatrix()); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); #else // AG-HACK throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); // TODO: no, yagni #endif #endif } /// /// Replaces this path with curves that enclose the area that is filled when this path is drawn /// by the specified pen. /// public void Widen(XPen pen, XMatrix matrix, double flatness) { #if CORE // Just do nothing. #endif #if CORE__ throw new NotImplementedException("XGraphicsPath.Widen"); #endif #if GDI try { Lock.EnterGdiPlus(); _gdipPath.Widen(pen.RealizeGdiPen(), matrix.ToGdiMatrix(), (float)flatness); } finally { Lock.ExitGdiPlus(); } #endif #if WPF || NETFX_CORE #if !SILVERLIGHT && !NETFX_CORE _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); #else // AG-HACK throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); // TODO: no, yagni #endif #endif } /// /// Grants access to internal objects of this class. /// public XGraphicsPathInternals Internals { get { return new XGraphicsPathInternals(this); } } #if CORE /// /// Gets access to underlying Core graphics path. /// internal CoreGraphicsPath _corePath; #endif #if GDI /// /// Gets access to underlying GDI+ graphics path. /// internal GraphicsPath _gdipPath; #endif #if WPF || NETFX_CORE /// /// Gets access to underlying WPF/WinRT path geometry. /// internal PathGeometry _pathGeometry; #endif } }