RedactPolygon.cs
- //
- // This code is part of Document Solutions for PDF demos.
- // Copyright (c) MESCIUS inc. All rights reserved.
- //
- using System;
- using System.IO;
- using System.Numerics;
- using System.Linq;
- using System.Drawing;
- using System.Text.RegularExpressions;
- using GrapeCity.Documents.Pdf;
- using GrapeCity.Documents.Pdf.Annotations;
- using GrapeCity.Documents.Pdf.TextMap;
- using GrapeCity.Documents.Pdf.AcroForms;
- using GrapeCity.Documents.Drawing;
- using GrapeCity.Documents.Common;
-
- namespace DsPdfWeb.Demos
- {
- // This sample demonstrates the use of RedactAnnotation.AddPolygon() method
- // which allows you to add arbitrary polygon shapes to the redacted area.
- // For example, you can add a star-shaped area to the redact annotation,
- // and all content within that area will be erased when the redact annotation is applied.
- // To show exactly what areas have been erased, we set the OverlayFillColor
- // of the redact annotations to different solid colors, so that in the redacted
- // PDF the colored polygons indicate the areas from which the original content
- // has been erased when the redacts were applied.
- //
- // We use the first page of the PDF generated by the BalancedColumns sample
- // to perform the redacts, and for reference the original page without redacts
- // is duplicated as the second page of the resulting document.
- public class RedactPolygon
- {
- public int CreatePDF(Stream stream)
- {
- var doc = new GcPdfDocument();
- using var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "BalancedColumns.pdf"));
- // Load the original document (we will use its first page only):
- var tdoc = new GcPdfDocument();
- tdoc.Load(fs);
- // Get the first page:
- doc.MergeWithDocument(tdoc, new MergeDocumentOptions() { PagesRange = new OutputRange(1, 1) });
- var page = doc.Pages[0];
- // For reference, copy the first page:
- doc.Pages.ClonePage(0, 1);
-
- // Set up a layout grid:
- var grid = new
- {
- Cols = 2,
- Rows = 6,
- MarginX = 72,
- MarginY = 72,
- Radius = 36,
- StepX = (page.Size.Width - 72) / 2,
- StepY = (page.Size.Height - 144) / 6,
- };
- // Insertion point for next polygon to be redacted:
- var startIp = new PointF(page.Size.Width / 3, grid.MarginY + grid.StepY / 2);
- var ip = startIp;
- int ipIdx = 0;
- // Some pastels to use as redact fill colors :
- var fills = new Color[]
- {
- Color.FromArgb(unchecked((int)0xff70ae98)),
- Color.FromArgb(unchecked((int)0xffecbe7a)),
- Color.FromArgb(unchecked((int)0xffe58b88)),
- Color.FromArgb(unchecked((int)0xff9dabdd)),
- Color.FromArgb(unchecked((int)0xff9dabd0)),
- Color.FromArgb(unchecked((int)0xff38908f)),
- Color.FromArgb(unchecked((int)0xffb2ebe0)),
- Color.FromArgb(unchecked((int)0xff5e96ae)),
- Color.FromArgb(unchecked((int)0xffffbfa3)),
- Color.FromArgb(unchecked((int)0xffe08963)),
- Color.FromArgb(unchecked((int)0xff9799ba)),
- Color.FromArgb(unchecked((int)0xffbc85a3)),
- };
- var fillColor = fills[0];
- void nextIp()
- {
- if (++ipIdx % 2 != 0)
- {
- ip.X += grid.StepX;
- }
- else
- {
- ip.X = startIp.X;
- ip.Y += grid.StepY;
- }
- fillColor = fills[ipIdx];
- }
-
- // Layout helper setup done, now add some polygon areas to be redacted.
- // Note that the polygons specify the areas within which all original content
- // will be erased when the redact is applied. The redact fill colors are used
- // simply to visualize the redacted (erased) areas, the data within those areas
- // is completely erased after applying the redact, and cannot be retrieved
- // using low level PDF tools.
-
- // Pentagon:
- var pts = MakePolygon(ip, grid.Radius, 5, (float)-Math.PI / 2);
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Hexagon:
- pts = MakePolygon(ip, grid.Radius, 6, 0);
- // Distort the shape a bit:
- pts[0].X += 18;
- pts[3].X -= 72;
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Octagon:
- pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 8);
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Triangle:
- pts = MakePolygon(ip, grid.Radius, 3, (float)-Math.PI / 2);
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Pentagram:
- pts = MakePolygon(ip, grid.Radius, 5, (float)-Math.PI / 2);
- pts = new PointF[] { pts[0], pts[2], pts[4], pts[1], pts[3], };
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Same pentagram using FillMode.Winding (the default is FillMode.Alternate):
- pts = pts.Select(p_ => new PointF(p_.X + grid.StepX, p_.Y)).ToArray();
- AddPolygonToRedact(page, fillColor, pts, FillMode.Winding);
- nextIp();
-
- // Set up a simple oblique projection to draw a pyramid:
- var angle = Math.PI / 6;
- float s = (float)Math.Sin(angle);
- float c = (float)Math.Cos(angle);
- Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
- float hedge = grid.Radius; // 1/2 edge
- Func<Vector3, PointF> p3d = v_ => { var p_ = project(v_.X, v_.Y, v_.Z); p_.X += ip.X; p_.Y += ip.Y + hedge * 0.7f; return p_; };
- // 3d points forming a square pyramid:
- var pts3d = new Vector3[]
- {
- new Vector3(-hedge, -hedge, 0),
- new Vector3(hedge, -hedge, 0),
- new Vector3(hedge, hedge, 0),
- new Vector3(-hedge, hedge, 0),
- new Vector3(0, 0, hedge * 2),
- };
- // Project the points to draw the pyramid:
- pts = pts3d.Select(v_ => p3d(v_)).ToArray();
- // Visible edges:
- AddPolygonToRedact(page, fillColor, new PointF[] { pts[4], pts[1], pts[2], pts[3], pts[4], pts[2] });
- // For reference, invisible edges are: pts[0]..pts[4], pts[0]..pts[1], pts[0]..pts[3]
- nextIp();
-
- // Parallelogram:
- float dx = 10;
- pts = MakePolygon(ip, grid.Radius, 4, (float)-Math.PI / 4);
- pts[0].X += dx;
- pts[1].X -= dx;
- pts[2].X -= dx;
- pts[3].X += dx;
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Trapezoid:
- pts = MakePolygon(ip, grid.Radius, 4, (float)-Math.PI / 4);
- pts[0].X -= dx;
- pts[1].X += dx;
- pts[2].X -= dx;
- pts[3].X += dx;
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Hexagram:
- pts = MakePolygon(ip, grid.Radius, 6, (float)-Math.PI / 2);
- pts = new PointF[]
- {
- pts[0],
- Intersect(pts[0], pts[2], pts[5], pts[1]),
- pts[1],
- Intersect(pts[0], pts[2], pts[1], pts[3]),
- pts[2],
- Intersect(pts[1], pts[3], pts[2], pts[4]),
- pts[3],
- Intersect(pts[2], pts[4], pts[3], pts[5]),
- pts[4],
- Intersect(pts[4], pts[0], pts[3], pts[5]),
- pts[5],
- Intersect(pts[4], pts[0], pts[5], pts[1]),
- };
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Octagram:
- pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 2);
- pts = new PointF[]
- {
- pts[0],
- Intersect(pts[0], pts[2], pts[7], pts[1]),
- pts[1],
- Intersect(pts[1], pts[3], pts[0], pts[2]),
- pts[2],
- Intersect(pts[2], pts[4], pts[1], pts[3]),
- pts[3],
- Intersect(pts[2], pts[4], pts[3], pts[5]),
- pts[4],
- Intersect(pts[3], pts[5], pts[4], pts[6]),
- pts[5],
- Intersect(pts[5], pts[7], pts[4], pts[6]),
- pts[6],
- Intersect(pts[6], pts[0], pts[5], pts[7]),
- pts[7],
- Intersect(pts[6], pts[0], pts[1], pts[7]),
- };
- AddPolygonToRedact(page, fillColor, pts);
- nextIp();
-
- // Another octagram:
- pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 2);
- pts = new PointF[] { pts[0], pts[3], pts[6], pts[1], pts[4], pts[7], pts[2], pts[5] };
- AddPolygonToRedact(page, fillColor, pts);
-
- // Apply all redacts:
- doc.Redact();
-
- // Done:
- doc.Save(stream);
- return doc.Pages.Count;
- }
-
- // Finds the intersection of two infinite lines specified by
- // points a0..a1 and b0..b1.
- private static PointF Intersect(PointF a0, PointF a1, PointF b0, PointF b1)
- {
- // Math:
- // y = (x - a0.X) * qa + a0.Y
- // (x - a0.X) * qa + a0.Y = (x - b0.X) * qb + b0.Y
- // (x - a0.X) * qa - (x - b0.X) * qb = b0.Y - a0.Y
- // x = (b0.Y - a0.Y + a0.X * qa - b0.X * qb) / (qa - qb)
- var ret = PointF.Empty;
- var qa = (a1.Y - a0.Y) / (a1.X - a0.X);
- var qb = (b1.Y - b0.Y) / (b1.X - b0.X);
- // Deal with non-functions (vertical lines):
- if (!float.IsFinite(qa) || !float.IsFinite(qb))
- {
- if (float.IsFinite(qa) || a0.X == b0.X)
- ret.X = b0.X;
- else if (float.IsFinite(qb))
- ret.X = a0.X;
- else
- ret.X = float.NaN;
- }
- else if (qa == qb)
- ret.X = float.NaN;
- else
- ret.X = (b0.Y - a0.Y + a0.X * qa - b0.X * qb) / (qa - qb);
- if (float.IsFinite(qa))
- ret.Y = (ret.X - a0.X) * qa + a0.Y;
- else
- ret.Y = (ret.X - b0.X) * qb + b0.Y;
- return ret;
- }
-
- private static PointF[] MakePolygon(PointF center, float r, int n, float startAngle)
- {
- PointF[] pts = new PointF[n];
- for (int i = 0; i < n; ++i)
- pts[i] = new PointF(center.X + (float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + (float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)));
- return pts;
- }
-
- private static void AddPolygonToRedact(Page p, Color fillColor, PointF[] pts, FillMode fillMode = FillMode.Alternate)
- {
- var redact = new RedactAnnotation()
- {
- Page = p,
- OverlayFillColor = fillColor
- };
- redact.AddPolygon(pts, fillMode);
- // Debug:
- // p.Graphics.DrawPolygon(pts, Color.Magenta);
- }
- }
- }
-