#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.Collections.Generic; using System.Diagnostics; using PdfSharp.Internal; namespace PdfSharp.Drawing { /// /// Represents a graphics path that uses the same notation as GDI+. /// internal class CoreGraphicsPath { // Same values as GDI+ uses. const byte PathPointTypeStart = 0; // move const byte PathPointTypeLine = 1; // line const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). const byte PathPointTypeCloseSubpath = 0x80; // closed flag public CoreGraphicsPath() { } public CoreGraphicsPath(CoreGraphicsPath path) { _points = new List(path._points); _types = new List(path._types); } public void MoveOrLineTo(double x, double y) { // Make a MoveTo if there is no previous subpath or the previous subpath was closed. // Otherwise make a LineTo. if (_types.Count == 0 || (_types[_types.Count - 1] & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath) MoveTo(x, y); else LineTo(x, y, false); } public void MoveTo(double x, double y) { _points.Add(new XPoint(x, y)); _types.Add(PathPointTypeStart); } public void LineTo(double x, double y, bool closeSubpath) { if (_points.Count > 0 && _points[_points.Count - 1].Equals(new XPoint(x, y))) return; _points.Add(new XPoint(x, y)); _types.Add((byte)(PathPointTypeLine | (closeSubpath ? PathPointTypeCloseSubpath : 0))); } public void BezierTo(double x1, double y1, double x2, double y2, double x3, double y3, bool closeSubpath) { _points.Add(new XPoint(x1, y1)); _types.Add(PathPointTypeBezier); _points.Add(new XPoint(x2, y2)); _types.Add(PathPointTypeBezier); _points.Add(new XPoint(x3, y3)); _types.Add((byte)(PathPointTypeBezier | (closeSubpath ? PathPointTypeCloseSubpath : 0))); } /// /// Adds an arc that fills exactly one quadrant (quarter) of an ellipse. /// Just a quick hack to draw rounded rectangles before AddArc is fully implemented. /// public void QuadrantArcTo(double x, double y, double width, double height, int quadrant, bool clockwise) { if (width < 0) throw new ArgumentOutOfRangeException("width"); if (height < 0) throw new ArgumentOutOfRangeException("height"); double w = Const.κ * width; double h = Const.κ * height; double x1, y1, x2, y2, x3, y3; switch (quadrant) { case 1: if (clockwise) { x1 = x + w; y1 = y - height; x2 = x + width; y2 = y - h; x3 = x + width; y3 = y; } else { x1 = x + width; y1 = y - h; x2 = x + w; y2 = y - height; x3 = x; y3 = y - height; } break; case 2: if (clockwise) { x1 = x - width; y1 = y - h; x2 = x - w; y2 = y - height; x3 = x; y3 = y - height; } else { x1 = x - w; y1 = y - height; x2 = x - width; y2 = y - h; x3 = x - width; y3 = y; } break; case 3: if (clockwise) { x1 = x - w; y1 = y + height; x2 = x - width; y2 = y + h; x3 = x - width; y3 = y; } else { x1 = x - width; y1 = y + h; x2 = x - w; y2 = y + height; x3 = x; y3 = y + height; } break; case 4: if (clockwise) { x1 = x + width; y1 = y + h; x2 = x + w; y2 = y + height; x3 = x; y3 = y + height; } else { x1 = x + w; y1 = y + height; x2 = x + width; y2 = y + h; x3 = x + width; y3 = y; } break; default: throw new ArgumentOutOfRangeException("quadrant"); } BezierTo(x1, y1, x2, y2, x3, y3, false); } /// /// Closes the current subpath. /// public void CloseSubpath() { int count = _types.Count; if (count > 0) _types[count - 1] |= PathPointTypeCloseSubpath; } /// /// Gets or sets the current fill mode (alternate or winding). /// XFillMode FillMode { get { return _fillMode; } set { _fillMode = value; } } XFillMode _fillMode; public void AddArc(double x, double y, double width, double height, double startAngle, double sweepAngle) { XMatrix matrix = XMatrix.Identity; List points = GeometryHelper.BezierCurveFromArc(x, y, width, height, startAngle, sweepAngle, PathStart.MoveTo1st, ref matrix); int count = points.Count; Debug.Assert((count + 2) % 3 == 0); MoveOrLineTo(points[0].X, points[0].Y); for (int idx = 1; idx < count; idx += 3) BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, points[idx + 2].X, points[idx + 2].Y, false); } public void AddArc(XPoint point1, XPoint point2, XSize size, double rotationAngle, bool isLargeArg, XSweepDirection sweepDirection) { List points = GeometryHelper.BezierCurveFromArc(point1, point2, size, rotationAngle, isLargeArg, sweepDirection == XSweepDirection.Clockwise, PathStart.MoveTo1st); int count = points.Count; Debug.Assert((count + 2) % 3 == 0); MoveOrLineTo(points[0].X, points[0].Y); for (int idx = 1; idx < count; idx += 3) BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, points[idx + 2].X, points[idx + 2].Y, false); } public void AddCurve(XPoint[] points, double tension) { int count = points.Length; if (count < 2) throw new ArgumentException("AddCurve requires two or more points.", "points"); tension /= 3; MoveOrLineTo(points[0].X, points[0].Y); if (count == 2) { //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); ToCurveSegment(points[0], points[0], points[1], points[1], tension); } else { //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension)); ToCurveSegment(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)); ToCurveSegment(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)); ToCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension); } } ///// ///// Appends a Bézier curve for a cardinal spline through pt1 and pt2. ///// //void ToCurveSegment(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double tension3, bool closeSubpath) //{ // BezierTo( // x1 + tension3 * (x2 - x0), y1 + tension3 * (y2 - y0), // x2 - tension3 * (x3 - x1), y2 - tension3 * (y3 - y1), // x2, y2, closeSubpath); //} void ToCurveSegment(XPoint pt0, XPoint pt1, XPoint pt2, XPoint pt3, double tension3) { BezierTo( pt1.X + tension3 * (pt2.X - pt0.X), pt1.Y + tension3 * (pt2.Y - pt0.Y), pt2.X - tension3 * (pt3.X - pt1.X), pt2.Y - tension3 * (pt3.Y - pt1.Y), pt2.X, pt2.Y, false); } /// /// Gets the path points in GDI+ style. /// public XPoint[] PathPoints { get { return _points.ToArray(); } } /// /// Gets the path types in GDI+ style. /// public byte[] PathTypes { get { return _types.ToArray(); } } readonly List _points = new List(); readonly List _types = new List(); } }