RedactPolygon.vb
'''' This code is part of Document Solutions for PDF .NET demos.'' Copyright (c) MESCIUS inc. All rights reserved.''Imports SystemImports System.IOImports System.NumericsImports System.LinqImports System.DrawingImports GrapeCity.Documents.PdfImports GrapeCity.Documents.Pdf.AnnotationsImports GrapeCity.Documents.DrawingImports GrapeCity.Documents.Common
'' 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 Function CreatePDF(ByVal stream As Stream) As Integer Dim doc = New GcPdfDocument() Using fs = File.OpenRead(Path.Combine("Resources", "PDFs", "BalancedColumns.pdf")) '' Load the original document (we will use its first page only): Dim tdoc = New GcPdfDocument() tdoc.Load(fs) ' Get the first page: doc.MergeWithDocument(tdoc, New MergeDocumentOptions() With {.PagesRange = New OutputRange(1, 1)}) Dim page = doc.Pages(0) ' For reference, copy the first page: doc.Pages.ClonePage(0, 1)
'' Set up a layout grid: Dim grid = New With { .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: Dim startIp = New PointF(page.Size.Width / 3, grid.MarginY + grid.StepY / 2) Dim ip = startIp Dim ipIdx = 0 '' Some pastels to use as redact fill colors: Dim fills = New Color() { Color.FromArgb(&HFF70AE98), Color.FromArgb(&HFFECBE7A), Color.FromArgb(&HFFE58B88), Color.FromArgb(&HFF9DABDD), Color.FromArgb(&HFF9DABD0), Color.FromArgb(&HFF38908F), Color.FromArgb(&HFFB2EBE0), Color.FromArgb(&HFF5E96AE), Color.FromArgb(&HFFFFBFA3), Color.FromArgb(&HFFE08963), Color.FromArgb(&HFF9799BA), Color.FromArgb(&HFFBC85A3) } Dim fillColor = fills(0)
Dim nextIp As Action = Sub() ipIdx += 1 If ipIdx Mod 2 <> 0 Then ip.X += CSng(grid.StepX) Else ip.X = startIp.X ip.Y += CSng(grid.StepY) End If fillColor = fills(ipIdx) End Sub
'' 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: Dim pts = MakePolygon(ip, grid.Radius, 5, CSng(-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, CSng(-Math.PI / 8)) AddPolygonToRedact(page, fillColor, pts) nextIp()
' Triangle: pts = MakePolygon(ip, grid.Radius, 3, CSng(-Math.PI / 2)) AddPolygonToRedact(page, fillColor, pts) nextIp()
' Pentagram: pts = MakePolygon(ip, grid.Radius, 5, CSng(-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(Function(p_) New PointF(p_.X + CSng(grid.StepX), p_.Y)).ToArray() AddPolygonToRedact(page, fillColor, pts, FillMode.Winding) nextIp()
'' Set up a simple oblique projection to draw a pyramid: Dim angle = Math.PI / 6 Dim s As Single = CSng(Math.Sin(angle)) Dim c As Single = CSng(Math.Cos(angle)) Dim project As Func(Of Single, Single, Single, PointF) = Function(x_, y_, z_) New PointF(x_ - c * y_ * 0.5F, -(z_ - s * y_ * 0.5F)) Dim hedge As Single = grid.Radius Dim p3d As Func(Of Vector3, PointF) = Function(v_) Dim p_ = project(v_.X, v_.Y, v_.Z) p_.X += ip.X p_.Y += ip.Y + hedge * 0.7F Return p_ End Function ' 3d points forming a square pyramid: Dim 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(Function(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: Dim dx As Single = 10 pts = MakePolygon(ip, grid.Radius, 4, CSng(-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, CSng(-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, CSng(-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, CSng(-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, CSng(-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 End Using End Function
'' Finds the intersection of two infinite lines specified by '' points a0..a1 and b0..b1. Private Shared Function Intersect(a0 As PointF, a1 As PointF, b0 As PointF, b1 As PointF) As PointF ' 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) Dim ret = PointF.Empty Dim qa = (a1.Y - a0.Y) / (a1.X - a0.X) Dim qb = (b1.Y - b0.Y) / (b1.X - b0.X) ' Deal with non-functions (vertical lines): If Not Single.IsFinite(qa) OrElse Not Single.IsFinite(qb) Then If Single.IsFinite(qa) OrElse a0.X = b0.X Then ret.X = b0.X ElseIf Single.IsFinite(qb) Then ret.X = a0.X Else ret.X = Single.NaN End If ElseIf qa = qb Then ret.X = Single.NaN Else ret.X = (b0.Y - a0.Y + a0.X * qa - b0.X * qb) / (qa - qb) End If If Single.IsFinite(qa) Then ret.Y = (ret.X - a0.X) * qa + a0.Y Else ret.Y = (ret.X - b0.X) * qb + b0.Y End If Return ret End Function
Private Shared Function MakePolygon(center As PointF, r As Single, n As Integer, startAngle As Single) As PointF() Dim pts = New PointF(n - 1) {} For i = 0 To n - 1 pts(i) = New PointF(center.X + CSng(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + CSng(r * Math.Sin(startAngle + 2 * Math.PI * i / n))) Next Return pts End Function
Private Shared Sub AddPolygonToRedact(p As Page, fillColor As Color, pts As PointF(), Optional fillMode As FillMode = FillMode.Alternate) Dim redact = New RedactAnnotation() With { .Page = p, .OverlayFillColor = fillColor } redact.AddPolygon(pts, fillMode) End SubEnd Class