* Update *
Found a solution using Clipper library. Solution added as answer. New / better / easier ideas are still welcome though!
Given a path like this:
I want to create a path surrounding this path with a given distance, e.g. 1 cm. The following sketch demonstrates that - the red path surrounds the black path with a distance of 1 cm.
How can this be done in a generic way using PDFSharp? (Meaning I want to finally draw it with PDFSharp, I don't care where the calculations are done) Here is the code for the black path:
// helper for easily getting an XPoint in centimeters
private XPoint cmPoint(double x, double y)
{
return new XPoint(
XUnit.FromCentimeter(x),
XUnit.FromCentimeter(y)
);
}
// the path to be drawn
private XGraphicsPath getMyPath()
{
XGraphicsPath path = new XGraphicsPath();
XPoint[] points = new XPoint[3];
points[0] = cmPoint(0, 0);
points[1] = cmPoint(5, 2);
points[2] = cmPoint(10,0);
path.AddCurve(points);
path.AddLine(cmPoint(10, 0), cmPoint(10, 10));
path.AddLine(cmPoint(10, 10), cmPoint(0, 10));
path.CloseFigure();
return path;
}
// generate the PDF file
private void button3_Click(object sender, RoutedEventArgs e)
{
// Create a temporary file
string filename = String.Format("{0}_tempfile.pdf", Guid.NewGuid().ToString("D").ToUpper());
XPen penBlack = new XPen(XColors.Black, 1);
XPen penRed = new XPen(XColors.Red, 1);
PdfDocument pdfDocument = new PdfDocument();
PdfPage page = pdfDocument.AddPage();
page.Size = PdfSharp.PageSize.A1;
XGraphics gfx = XGraphics.FromPdfPage(page);
//give us some space to the left and top
gfx.TranslateTransform(XUnit.FromCentimeter(3), XUnit.FromCentimeter(3));
// draw the desired path
gfx.DrawPath(penBlack, getMyPath());
// Save the pdfDocument...
pdfDocument.Save(filename);
// ...and start a viewer
Process.Start(filename);
}
Thanks for any help on this topic!
You can use Widen()
function, which replaces the path with curves that enclose the area that is filled when the path is drawn by a specified pen, adding an additional outline to the path.
This function receives as parameter a XPen
, so you can create this XPen
using the desired offset as width and an outer path will be added at a constant distance (pen's width).
XGraphicsPath
class is in fact a wrapper of System.Drawing.Drawing2D.GraphicsPath
, so you can use Widen()
function in XGraphicsPath
, get the internal object and iterate on it using GraphicsPathIterator
class to get the path added.
This method will do the job:
public XGraphicsPath GetSurroundPath(XGraphicsPath path, double width)
{
XGraphicsPath container = new XGraphicsPath();
container.StartFigure();
container.AddPath(path, false);
container.CloseFigure();
var penOffset = new XPen(XColors.Black, width);
container.StartFigure();
container.Widen(penOffset);
container.CloseFigure();
var iterator = new GraphicsPathIterator(container.Internals.GdiPath);
bool isClosed;
var outline = new XGraphicsPath();
iterator.NextSubpath(outline.Internals.GdiPath, out isClosed);
return outline;
}
You can handle level of flatness in curves using the overload Widen(XPen pen, XMatrix matrix, double flatness)
. Doing this call container.Widen(penOffset, XMatrix.Identity, 0.05);
results in more rounded edges.
Then draw an outer path using this function:
string filename = String.Format("{0}_tempfile.pdf", Guid.NewGuid().ToString("D").ToUpper());
XPen penBlack = new XPen(XColors.Black, 1);
XPen penRed = new XPen(XColors.Red, 1);
PdfDocument pdfDocument = new PdfDocument();
PdfPage page = pdfDocument.AddPage();
page.Size = PdfSharp.PageSize.A1;
XGraphics gfx = XGraphics.FromPdfPage(page);
//give us some space to the left and top
gfx.TranslateTransform(XUnit.FromCentimeter(3), XUnit.FromCentimeter(3));
var path = getMyPath();
// draw the desired path
gfx.DrawPath(penBlack, path);
gfx.DrawPath(penRed, GetSurroundPath(path, XUnit.FromCentimeter(1).Point));
// Save the pdfDocument...
pdfDocument.Save(filename);
// ...and start a viewer
Process.Start(filename);
This is what you get:
Another way may be using reflection to retrieve internal Pen
in XPen
and setup CompoundArray
property. This allows you draws parallel lines and spaces. Using this property you can do something like this:
But the problem is that you can only use one color, anyway this is just an idea, I have not tried in PDFsharp
Also, you should search for offset polyline curves or offsetting polygon algorithms.
This can be done using Clipper
double scale = 1024.0;
List<IntPoint> points = new List<IntPoint>();
points.Add(new IntPoint(0*scale, 0*scale));
points.Add(new IntPoint(5*scale, 2*scale));
points.Add(new IntPoint(10*scale, 0*scale));
points.Add(new IntPoint(10*scale, 10*scale));
points.Add(new IntPoint(0*scale, 10*scale));
points.Reverse();
List<List<IntPoint>> solution = new List<List<IntPoint>>();
ClipperOffset co = new ClipperOffset();
co.AddPath(points, JoinType.jtMiter, EndType.etClosedPolygon);
co.Execute(ref solution, 1 * scale);
foreach (IntPoint point in solution[0])
{
Console.WriteLine("OUTPUT: " + point.X + "/" + point.Y + " -> " + point.X/scale + "/" + point.Y/scale);
}
And the output:
OUTPUT: 11264/11264 -> 11/11
OUTPUT: -1024/11264 -> -1/11
OUTPUT: -1024/-1512 -> -1/-1,4765625
OUTPUT: 5120/945 -> 5/0,9228515625
OUTPUT: 11264/-1512 -> 11/-1,4765625
Drawn original and offset path:
This is still not perfect for various mathematical reasons, but already quite good.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With