RedactPolygon.cs
  1. //
  2. // This code is part of Document Solutions for PDF demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Numerics;
  8. using System.Linq;
  9. using System.Drawing;
  10. using System.Text.RegularExpressions;
  11. using GrapeCity.Documents.Pdf;
  12. using GrapeCity.Documents.Pdf.Annotations;
  13. using GrapeCity.Documents.Pdf.TextMap;
  14. using GrapeCity.Documents.Pdf.AcroForms;
  15. using GrapeCity.Documents.Drawing;
  16. using GrapeCity.Documents.Common;
  17.  
  18. namespace DsPdfWeb.Demos
  19. {
  20. // This sample demonstrates the use of RedactAnnotation.AddPolygon() method
  21. // which allows you to add arbitrary polygon shapes to the redacted area.
  22. // For example, you can add a star-shaped area to the redact annotation,
  23. // and all content within that area will be erased when the redact annotation is applied.
  24. // To show exactly what areas have been erased, we set the OverlayFillColor
  25. // of the redact annotations to different solid colors, so that in the redacted
  26. // PDF the colored polygons indicate the areas from which the original content
  27. // has been erased when the redacts were applied.
  28. //
  29. // We use the first page of the PDF generated by the BalancedColumns sample
  30. // to perform the redacts, and for reference the original page without redacts
  31. // is duplicated as the second page of the resulting document.
  32. public class RedactPolygon
  33. {
  34. public int CreatePDF(Stream stream)
  35. {
  36. var doc = new GcPdfDocument();
  37. using var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "BalancedColumns.pdf"));
  38. // Load the original document (we will use its first page only):
  39. var tdoc = new GcPdfDocument();
  40. tdoc.Load(fs);
  41. // Get the first page:
  42. doc.MergeWithDocument(tdoc, new MergeDocumentOptions() { PagesRange = new OutputRange(1, 1) });
  43. var page = doc.Pages[0];
  44. // For reference, copy the first page:
  45. doc.Pages.ClonePage(0, 1);
  46.  
  47. // Set up a layout grid:
  48. var grid = new
  49. {
  50. Cols = 2,
  51. Rows = 6,
  52. MarginX = 72,
  53. MarginY = 72,
  54. Radius = 36,
  55. StepX = (page.Size.Width - 72) / 2,
  56. StepY = (page.Size.Height - 144) / 6,
  57. };
  58. // Insertion point for next polygon to be redacted:
  59. var startIp = new PointF(page.Size.Width / 3, grid.MarginY + grid.StepY / 2);
  60. var ip = startIp;
  61. int ipIdx = 0;
  62. // Some pastels to use as redact fill colors :
  63. var fills = new Color[]
  64. {
  65. Color.FromArgb(unchecked((int)0xff70ae98)),
  66. Color.FromArgb(unchecked((int)0xffecbe7a)),
  67. Color.FromArgb(unchecked((int)0xffe58b88)),
  68. Color.FromArgb(unchecked((int)0xff9dabdd)),
  69. Color.FromArgb(unchecked((int)0xff9dabd0)),
  70. Color.FromArgb(unchecked((int)0xff38908f)),
  71. Color.FromArgb(unchecked((int)0xffb2ebe0)),
  72. Color.FromArgb(unchecked((int)0xff5e96ae)),
  73. Color.FromArgb(unchecked((int)0xffffbfa3)),
  74. Color.FromArgb(unchecked((int)0xffe08963)),
  75. Color.FromArgb(unchecked((int)0xff9799ba)),
  76. Color.FromArgb(unchecked((int)0xffbc85a3)),
  77. };
  78. var fillColor = fills[0];
  79. void nextIp()
  80. {
  81. if (++ipIdx % 2 != 0)
  82. {
  83. ip.X += grid.StepX;
  84. }
  85. else
  86. {
  87. ip.X = startIp.X;
  88. ip.Y += grid.StepY;
  89. }
  90. fillColor = fills[ipIdx];
  91. }
  92.  
  93. // Layout helper setup done, now add some polygon areas to be redacted.
  94. // Note that the polygons specify the areas within which all original content
  95. // will be erased when the redact is applied. The redact fill colors are used
  96. // simply to visualize the redacted (erased) areas, the data within those areas
  97. // is completely erased after applying the redact, and cannot be retrieved
  98. // using low level PDF tools.
  99.  
  100. // Pentagon:
  101. var pts = MakePolygon(ip, grid.Radius, 5, (float)-Math.PI / 2);
  102. AddPolygonToRedact(page, fillColor, pts);
  103. nextIp();
  104.  
  105. // Hexagon:
  106. pts = MakePolygon(ip, grid.Radius, 6, 0);
  107. // Distort the shape a bit:
  108. pts[0].X += 18;
  109. pts[3].X -= 72;
  110. AddPolygonToRedact(page, fillColor, pts);
  111. nextIp();
  112.  
  113. // Octagon:
  114. pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 8);
  115. AddPolygonToRedact(page, fillColor, pts);
  116. nextIp();
  117.  
  118. // Triangle:
  119. pts = MakePolygon(ip, grid.Radius, 3, (float)-Math.PI / 2);
  120. AddPolygonToRedact(page, fillColor, pts);
  121. nextIp();
  122.  
  123. // Pentagram:
  124. pts = MakePolygon(ip, grid.Radius, 5, (float)-Math.PI / 2);
  125. pts = new PointF[] { pts[0], pts[2], pts[4], pts[1], pts[3], };
  126. AddPolygonToRedact(page, fillColor, pts);
  127. nextIp();
  128.  
  129. // Same pentagram using FillMode.Winding (the default is FillMode.Alternate):
  130. pts = pts.Select(p_ => new PointF(p_.X + grid.StepX, p_.Y)).ToArray();
  131. AddPolygonToRedact(page, fillColor, pts, FillMode.Winding);
  132. nextIp();
  133.  
  134. // Set up a simple oblique projection to draw a pyramid:
  135. var angle = Math.PI / 6;
  136. float s = (float)Math.Sin(angle);
  137. float c = (float)Math.Cos(angle);
  138. Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
  139. float hedge = grid.Radius; // 1/2 edge
  140. 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_; };
  141. // 3d points forming a square pyramid:
  142. var pts3d = new Vector3[]
  143. {
  144. new Vector3(-hedge, -hedge, 0),
  145. new Vector3(hedge, -hedge, 0),
  146. new Vector3(hedge, hedge, 0),
  147. new Vector3(-hedge, hedge, 0),
  148. new Vector3(0, 0, hedge * 2),
  149. };
  150. // Project the points to draw the pyramid:
  151. pts = pts3d.Select(v_ => p3d(v_)).ToArray();
  152. // Visible edges:
  153. AddPolygonToRedact(page, fillColor, new PointF[] { pts[4], pts[1], pts[2], pts[3], pts[4], pts[2] });
  154. // For reference, invisible edges are: pts[0]..pts[4], pts[0]..pts[1], pts[0]..pts[3]
  155. nextIp();
  156.  
  157. // Parallelogram:
  158. float dx = 10;
  159. pts = MakePolygon(ip, grid.Radius, 4, (float)-Math.PI / 4);
  160. pts[0].X += dx;
  161. pts[1].X -= dx;
  162. pts[2].X -= dx;
  163. pts[3].X += dx;
  164. AddPolygonToRedact(page, fillColor, pts);
  165. nextIp();
  166.  
  167. // Trapezoid:
  168. pts = MakePolygon(ip, grid.Radius, 4, (float)-Math.PI / 4);
  169. pts[0].X -= dx;
  170. pts[1].X += dx;
  171. pts[2].X -= dx;
  172. pts[3].X += dx;
  173. AddPolygonToRedact(page, fillColor, pts);
  174. nextIp();
  175.  
  176. // Hexagram:
  177. pts = MakePolygon(ip, grid.Radius, 6, (float)-Math.PI / 2);
  178. pts = new PointF[]
  179. {
  180. pts[0],
  181. Intersect(pts[0], pts[2], pts[5], pts[1]),
  182. pts[1],
  183. Intersect(pts[0], pts[2], pts[1], pts[3]),
  184. pts[2],
  185. Intersect(pts[1], pts[3], pts[2], pts[4]),
  186. pts[3],
  187. Intersect(pts[2], pts[4], pts[3], pts[5]),
  188. pts[4],
  189. Intersect(pts[4], pts[0], pts[3], pts[5]),
  190. pts[5],
  191. Intersect(pts[4], pts[0], pts[5], pts[1]),
  192. };
  193. AddPolygonToRedact(page, fillColor, pts);
  194. nextIp();
  195.  
  196. // Octagram:
  197. pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 2);
  198. pts = new PointF[]
  199. {
  200. pts[0],
  201. Intersect(pts[0], pts[2], pts[7], pts[1]),
  202. pts[1],
  203. Intersect(pts[1], pts[3], pts[0], pts[2]),
  204. pts[2],
  205. Intersect(pts[2], pts[4], pts[1], pts[3]),
  206. pts[3],
  207. Intersect(pts[2], pts[4], pts[3], pts[5]),
  208. pts[4],
  209. Intersect(pts[3], pts[5], pts[4], pts[6]),
  210. pts[5],
  211. Intersect(pts[5], pts[7], pts[4], pts[6]),
  212. pts[6],
  213. Intersect(pts[6], pts[0], pts[5], pts[7]),
  214. pts[7],
  215. Intersect(pts[6], pts[0], pts[1], pts[7]),
  216. };
  217. AddPolygonToRedact(page, fillColor, pts);
  218. nextIp();
  219.  
  220. // Another octagram:
  221. pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 2);
  222. pts = new PointF[] { pts[0], pts[3], pts[6], pts[1], pts[4], pts[7], pts[2], pts[5] };
  223. AddPolygonToRedact(page, fillColor, pts);
  224.  
  225. // Apply all redacts:
  226. doc.Redact();
  227.  
  228. // Done:
  229. doc.Save(stream);
  230. return doc.Pages.Count;
  231. }
  232.  
  233. // Finds the intersection of two infinite lines specified by
  234. // points a0..a1 and b0..b1.
  235. private static PointF Intersect(PointF a0, PointF a1, PointF b0, PointF b1)
  236. {
  237. // Math:
  238. // y = (x - a0.X) * qa + a0.Y
  239. // (x - a0.X) * qa + a0.Y = (x - b0.X) * qb + b0.Y
  240. // (x - a0.X) * qa - (x - b0.X) * qb = b0.Y - a0.Y
  241. // x = (b0.Y - a0.Y + a0.X * qa - b0.X * qb) / (qa - qb)
  242. var ret = PointF.Empty;
  243. var qa = (a1.Y - a0.Y) / (a1.X - a0.X);
  244. var qb = (b1.Y - b0.Y) / (b1.X - b0.X);
  245. // Deal with non-functions (vertical lines):
  246. if (!float.IsFinite(qa) || !float.IsFinite(qb))
  247. {
  248. if (float.IsFinite(qa) || a0.X == b0.X)
  249. ret.X = b0.X;
  250. else if (float.IsFinite(qb))
  251. ret.X = a0.X;
  252. else
  253. ret.X = float.NaN;
  254. }
  255. else if (qa == qb)
  256. ret.X = float.NaN;
  257. else
  258. ret.X = (b0.Y - a0.Y + a0.X * qa - b0.X * qb) / (qa - qb);
  259. if (float.IsFinite(qa))
  260. ret.Y = (ret.X - a0.X) * qa + a0.Y;
  261. else
  262. ret.Y = (ret.X - b0.X) * qb + b0.Y;
  263. return ret;
  264. }
  265.  
  266. private static PointF[] MakePolygon(PointF center, float r, int n, float startAngle)
  267. {
  268. PointF[] pts = new PointF[n];
  269. for (int i = 0; i < n; ++i)
  270. 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)));
  271. return pts;
  272. }
  273.  
  274. private static void AddPolygonToRedact(Page p, Color fillColor, PointF[] pts, FillMode fillMode = FillMode.Alternate)
  275. {
  276. var redact = new RedactAnnotation()
  277. {
  278. Page = p,
  279. OverlayFillColor = fillColor
  280. };
  281. redact.AddPolygon(pts, fillMode);
  282. // Debug:
  283. // p.Graphics.DrawPolygon(pts, Color.Magenta);
  284. }
  285. }
  286. }
  287.