RedactPolygon.cs
//
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © 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
{
// このサンプルでは、RedactAnnotation.AddPolygon()メソッドを使用して、
// 任意の多角形を墨消し注釈として追加する方法を示しています。
// サンプル BalancedColumns で生成された PDF の最初のページを読み込み、
// そのページを多角形にて墨消しする、墨消し注釈を追加します。
public class RedactPolygonQ
{
public int CreatePDF(Stream stream)
{
var doc = new GcPdfDocument();
using (var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "BalancedColumns.pdf")))
{
// ドキュメントを読み込みます(最初のページのみを使用)。
var tdoc = new GcPdfDocument();
tdoc.Load(fs);
// 最初のページを取得します。
doc.MergeWithDocument(tdoc, new MergeDocumentOptions() { PagesRange = new OutputRange(1, 1) });
var page = doc.Pages[0];
// レイアウトグリッドを設定します。
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,
};
// 墨消しする多角形の挿入ポイントを指定します。
var startIp = new PointF(page.Size.Width / 3, grid.MarginY + grid.StepY / 2);
var ip = startIp;
int ipIdx = 0;
// 塗りつぶし色として パステルカラー を使用します。
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];
}
// レイアウトの設定が完了したので、墨消しする多角形を追加します。
// 五角形
var pts = MakePolygon(ip, grid.Radius, 5, (float)-Math.PI / 2);
AddPolygonToRedact(page, fillColor, pts);
nextIp();
// 六角形
pts = MakePolygon(ip, grid.Radius, 6, 0);
// 少し形を歪ませます。
pts[0].X += 18;
pts[3].X -= 72;
AddPolygonToRedact(page, fillColor, pts);
nextIp();
// 八角形
pts = MakePolygon(ip, grid.Radius, 8, (float)-Math.PI / 8);
AddPolygonToRedact(page, fillColor, pts);
nextIp();
// 三角形
pts = MakePolygon(ip, grid.Radius, 3, (float)-Math.PI / 2);
AddPolygonToRedact(page, fillColor, pts);
nextIp();
// 五芒星
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();
// FillMode.Winding(デフォルトはFillMode.Alternate)を使用した同じ五芒星
pts = pts.Select(p_ => new PointF(p_.X + grid.StepX, p_.Y)).ToArray();
AddPolygonToRedact(page, fillColor, pts, FillMode.Winding);
nextIp();
// 単純な斜投影を設定してピラミッドを描画します。
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
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ポイントを指定します。
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),
};
// ポイントを投影してピラミッドを描画します。
pts = pts3d.Select(v_ => p3d(v_)).ToArray();
// 表示される辺
AddPolygonToRedact(page, fillColor, new PointF[] { pts[4], pts[1], pts[2], pts[3], pts[4], pts[2] });
// <参考>表示されない辺: pts[0]~pts[4], pts[0]~pts[1], pts[0]~pts[3]
nextIp();
// 平行四辺形
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();
// 台形
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();
// 六芒星
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();
// 八芒星
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();
// もう一つの八芒星
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);
// すべての墨消しを適用します。
doc.Redact();
// PDFドキュメントを保存します。
doc.Save(stream);
return doc.Pages.Count;
}
}
// ポイント a0~a1とポイントb0~b1で指定された2本の直線の交点を求めます。
private PointF Intersect(PointF a0, PointF a1, PointF b0, PointF b1)
{
// 計算式
// 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);
// 非関数(縦線)に対応
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 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 void AddPolygonToRedact(Page p, Color fillColor, PointF[] pts, FillMode fillMode = FillMode.Alternate)
{
var redact = new RedactAnnotation()
{
Page = p,
OverlayFillColor = fillColor
};
redact.AddPolygon(pts, fillMode);
// デバッグ用
// p.Graphics.DrawPolygon(pts, Color.Magenta);
}
}
}