ASCU_ALL/PrintPDF/PdfSharp/Drawing/CoreGraphicsPath.cs

303 lines
12 KiB
C#
Raw Permalink Normal View History

2020-09-04 12:49:15 +05:00
#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
{
/// <summary>
/// Represents a graphics path that uses the same notation as GDI+.
/// </summary>
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<XPoint>(path._points);
_types = new List<byte>(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)));
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// Closes the current subpath.
/// </summary>
public void CloseSubpath()
{
int count = _types.Count;
if (count > 0)
_types[count - 1] |= PathPointTypeCloseSubpath;
}
/// <summary>
/// Gets or sets the current fill mode (alternate or winding).
/// </summary>
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<XPoint> 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<XPoint> 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);
}
}
///// <summary>
///// Appends a Bézier curve for a cardinal spline through pt1 and pt2.
///// </summary>
//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);
}
/// <summary>
/// Gets the path points in GDI+ style.
/// </summary>
public XPoint[] PathPoints { get { return _points.ToArray(); } }
/// <summary>
/// Gets the path types in GDI+ style.
/// </summary>
public byte[] PathTypes { get { return _types.ToArray(); } }
readonly List<XPoint> _points = new List<XPoint>();
readonly List<byte> _types = new List<byte>();
}
}