SvgShapes.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.Drawing;
  8. using System.Numerics;
  9. using System.Linq;
  10. using System.Collections.Generic;
  11. using GrapeCity.Documents.Pdf;
  12. using GrapeCity.Documents.Text;
  13. using GrapeCity.Documents.Drawing;
  14. using GrapeCity.Documents.Svg;
  15. using GCTEXT = GrapeCity.Documents.Text;
  16. using GCDRAW = GrapeCity.Documents.Drawing;
  17. using DsPdfWeb.Demos.Common;
  18.  
  19. namespace DsPdfWeb.Demos
  20. {
  21. // This sample draws some geometric shapes using GcSvgDocument and GcGraphics.DrawSvg().
  22. // The code and the results are similar to the Shapes sample.
  23. public class SvgShapes
  24. {
  25. // Helper method to draw a polygon and a caption beneath it.
  26. // Can also be used to just calculate the points without actual drawing.
  27. // startAngle is for the first point, clockwise from (1,0).
  28. // Points are relative to 'center'.
  29. private SvgPoint[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, SvgPaint stroke, string caption = null)
  30. {
  31. const float Q = 1.4f;
  32. var plgn = new SvgPolygonElement();
  33. plgn.Points = new List<SvgPoint>(n);
  34. for (int i = 0; i < n; ++i)
  35. plgn.Points.Add(new SvgPoint(
  36. new SvgLength((float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage),
  37. new SvgLength((float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage)));
  38.  
  39. if (stroke != null)
  40. {
  41. plgn.Stroke = stroke;
  42. plgn.Fill = new SvgPaint(SvgColor.Transparent);
  43. var svgDoc = new GcSvgDocument();
  44. var svg = svgDoc.RootSvg;
  45. svg.Children.Add(plgn);
  46. g.DrawSvg(svgDoc, new RectangleF(center.X, center.Y, r * 2, r * 2));
  47. }
  48.  
  49. if (!string.IsNullOrEmpty(caption))
  50. DrawCaption(g, center, r, caption);
  51.  
  52. return plgn.Points.ToArray();
  53. }
  54. // Helper method to draw a caption beneath a shape:
  55. private void DrawCaption(GcGraphics g, PointF center, float r, string caption)
  56. {
  57. g.DrawString(caption,
  58. new TextFormat() { Font = StandardFonts.Times, FontSize = 10, },
  59. new RectangleF(center.X - r, center.Y + r, r * 2, 24),
  60. TextAlignment.Center, ParagraphAlignment.Center, false);
  61. }
  62. // Main entry point.
  63. public int CreatePDF(Stream stream)
  64. {
  65. var doc = new GcPdfDocument();
  66. var page = doc.Pages.Add();
  67. var g = page.Graphics;
  68. // Document header:
  69. g.DrawString("Shapes (SVG)",
  70. new TextFormat() { Font = StandardFonts.TimesBold, FontSize = 14, Underline = true, },
  71. new RectangleF(PointF.Empty, new SizeF(page.Size.Width, 44)),
  72. TextAlignment.Center, ParagraphAlignment.Far);
  73. // Pen used to draw shapes:
  74.  
  75. // Set up the helper layout grid:
  76. var grid = new
  77. {
  78. Cols = 3,
  79. Rows = 5,
  80. MarginX = 72,
  81. MarginY = 36,
  82. Radius = 36,
  83. StepX = (page.Size.Width - 144) / 3,
  84. StepY = (page.Size.Height - 72) / 5,
  85. };
  86.  
  87. // Insertion point of the next figure's center:
  88. var startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
  89. var ip = startIp;
  90. // Debug code to show the layout grid:
  91. /*
  92. var ipp = ip;
  93. for (int i = 0; i < grid.Cols; ++i)
  94. {
  95. ipp.Y = ip.Y;
  96. for (int j = 0; j < grid.Rows; ++j)
  97. {
  98. g.DrawRectangle(new RectangleF(ipp.X - grid.Radius, ipp.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), Color.LightGreen, 0.5f);
  99. ipp.Y += grid.StepY;
  100. }
  101. ipp.X += grid.StepX;
  102. }
  103. */
  104.  
  105. // Reusable SVG primitives:
  106. var len0 = new SvgLength(0, SvgLengthUnits.Percentage);
  107. var len10 = new SvgLength(10, SvgLengthUnits.Percentage);
  108. var len20 = new SvgLength(20, SvgLengthUnits.Percentage);
  109. var len30 = new SvgLength(30, SvgLengthUnits.Percentage);
  110. var len40 = new SvgLength(40, SvgLengthUnits.Percentage);
  111. var len50 = new SvgLength(50, SvgLengthUnits.Percentage);
  112. var len60 = new SvgLength(60, SvgLengthUnits.Percentage);
  113. var len70 = new SvgLength(70, SvgLengthUnits.Percentage);
  114. var len80 = new SvgLength(80, SvgLengthUnits.Percentage);
  115. var len90 = new SvgLength(90, SvgLengthUnits.Percentage);
  116. var len100 = new SvgLength(100, SvgLengthUnits.Percentage);
  117. var paintTrans = new SvgPaint(SvgColor.Transparent);
  118. var pntStroke0 = new SvgPaint(new SvgColor(Color.Green));
  119. var pntStroke0inv = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Green)));
  120. var pntFill0 = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Orange)));
  121. var pt0_0 = new SvgPoint(len0, len0);
  122. var pt100_100 = new SvgPoint(len100, len100);
  123.  
  124. // Circle:
  125. var svgDoc = new GcSvgDocument();
  126. var svg = svgDoc.RootSvg;
  127. svg.Children.Add(new SvgEllipseElement()
  128. {
  129. CenterX = len50,
  130. CenterY = len50,
  131. RadiusX = len50,
  132. RadiusY = len50,
  133. Fill = paintTrans,
  134. Stroke = pntStroke0,
  135. });
  136. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  137. DrawCaption(g, ip, grid.Radius, "Circle");
  138. ip.X += grid.StepX;
  139.  
  140. // Ellipse:
  141. svg.Children.Clear();
  142. svg.Children.Add(new SvgEllipseElement()
  143. {
  144. CenterX = len50,
  145. CenterY = len50,
  146. RadiusX = len50,
  147. RadiusY = len50,
  148. Fill = paintTrans,
  149. Stroke = pntStroke0,
  150. });
  151. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius * 1.4f, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4f, grid.Radius));
  152. DrawCaption(g, ip, grid.Radius, "Ellipse");
  153. ip.X += grid.StepX;
  154.  
  155. // Cylinder:
  156. svg.Children.Clear();
  157. float radX = grid.Radius / 1.4f;
  158. float radY = grid.Radius / 6;
  159. float height = grid.Radius * 1.8f;
  160. svg.Children.Add(new SvgEllipseElement()
  161. {
  162. CenterX = len50,
  163. CenterY = len10,
  164. RadiusX = len50,
  165. RadiusY = len10,
  166. Fill = paintTrans,
  167. Stroke = pntStroke0,
  168. });
  169. svg.Children.Add(new SvgEllipseElement()
  170. {
  171. CenterX = len50,
  172. CenterY = len90,
  173. RadiusX = len50,
  174. RadiusY = len10,
  175. Fill = pntFill0,
  176. Stroke = pntStroke0,
  177. });
  178. svg.Children.Add(new SvgLineElement()
  179. {
  180. X1 = len0,
  181. Y1 = len10,
  182. X2 = len0,
  183. Y2 = len90,
  184. Stroke = pntStroke0,
  185. });
  186. svg.Children.Add(new SvgLineElement()
  187. {
  188. X1 = len100,
  189. Y1 = len10,
  190. X2 = len100,
  191. Y2 = len90,
  192. Stroke = pntStroke0,
  193. });
  194. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  195. DrawCaption(g, ip, grid.Radius, "Cylinder");
  196. ip.X = startIp.X;
  197. ip.Y += grid.StepY;
  198.  
  199. // Square:
  200. DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, pntStroke0, "Square");
  201. ip.X += grid.StepX;
  202.  
  203. // Rectangle:
  204. svg.Children.Clear();
  205. svg.Children.Add(new SvgRectElement()
  206. {
  207. X = new SvgLength(-20, SvgLengthUnits.Percentage),
  208. Y = len20,
  209. Width = new SvgLength(140, SvgLengthUnits.Percentage),
  210. Height = len60,
  211. Stroke = pntStroke0,
  212. Fill = paintTrans,
  213. });
  214. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  215. DrawCaption(g, ip, grid.Radius, "Rectangle");
  216. ip.X += grid.StepX;
  217.  
  218. // Cube:
  219. svg.Children.Clear();
  220. svg.Children.Add(new SvgRectElement()
  221. {
  222. X = len10,
  223. Y = len10,
  224. Width = len60,
  225. Height = len60,
  226. Stroke = pntStroke0,
  227. Fill = paintTrans,
  228. });
  229. svg.Children.Add(new SvgRectElement()
  230. {
  231. X = len30,
  232. Y = len30,
  233. Width = len60,
  234. Height = len60,
  235. Stroke = pntStroke0,
  236. Fill = paintTrans,
  237. });
  238. svg.Children.Add(new SvgLineElement()
  239. {
  240. X1 = len10,
  241. Y1 = len10,
  242. X2 = len30,
  243. Y2 = len30,
  244. Stroke = pntStroke0,
  245. });
  246. svg.Children.Add(new SvgLineElement()
  247. {
  248. X1 = len10,
  249. Y1 = len70,
  250. X2 = len30,
  251. Y2 = len90,
  252. Stroke = pntStroke0,
  253. });
  254. svg.Children.Add(new SvgLineElement()
  255. {
  256. X1 = len70,
  257. Y1 = len10,
  258. X2 = len90,
  259. Y2 = len30,
  260. Stroke = pntStroke0,
  261. });
  262. svg.Children.Add(new SvgLineElement()
  263. {
  264. X1 = len70,
  265. Y1 = len70,
  266. X2 = len90,
  267. Y2 = len90,
  268. Stroke = pntStroke0,
  269. });
  270. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  271. DrawCaption(g, ip, grid.Radius, "Cube");
  272. ip.X = startIp.X;
  273. ip.Y += grid.StepY;
  274.  
  275. // Pentagon:
  276. DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagon");
  277. ip.X += grid.StepX;
  278.  
  279. // Hexagon:
  280. // For sample sake, we apply a transform to make the hexagon wider and shorter:
  281. g.Transform = Matrix3x2.CreateScale(1.4f, 0.8f) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  282. DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pntStroke0, null);
  283. g.Transform = Matrix3x2.Identity;
  284. DrawCaption(g, ip, grid.Radius, "Hexagon");
  285. ip.X += grid.StepX;
  286.  
  287. // Octagon:
  288. DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pntStroke0, "Octagon");
  289. ip.X = startIp.X;
  290. ip.Y += grid.StepY;
  291.  
  292. // Triangle:
  293. DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pntStroke0, "Triangle");
  294. ip.X += grid.StepX;
  295.  
  296. // Filled pentagram:
  297. var svgPts = DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagram");
  298. svg.Children.Clear();
  299. svg.Children.Add(new SvgPolygonElement()
  300. {
  301. Points = new List<SvgPoint>()
  302. {
  303. svgPts[0], svgPts[2], svgPts[4], svgPts[1], svgPts[3],
  304. },
  305. Stroke = pntStroke0,
  306. Fill = pntFill0,
  307. FillRule = SvgFillRule.EvenOdd,
  308. });
  309. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  310. ip.X += grid.StepX;
  311.  
  312. // Set up a simple kind of oblique projection to draw a pyramid:
  313. var angle = Math.PI / 6;
  314. float s = (float)Math.Sin(angle);
  315. float c = (float)Math.Cos(angle);
  316. Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
  317. Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
  318. float hedge = grid.Radius; // 1/2 edge
  319. // Debug - draw the 3 axis:
  320. /*
  321. g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red);
  322. g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green);
  323. g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue);
  324. */
  325. // 3d points forming a square pyramid:
  326. var pts3d = new Vector3[]
  327. {
  328. new Vector3(-hedge, -hedge, 0),
  329. new Vector3(hedge, -hedge, 0),
  330. new Vector3(hedge, hedge, 0),
  331. new Vector3(-hedge, hedge, 0),
  332. new Vector3(0, 0, hedge * 2),
  333. };
  334. // Project the points to draw the pyramid:
  335. var pts = pts3d.Select(v_ => p3d(v_));
  336. svgPts = pts.Select(p_ => new SvgPoint(new SvgLength(p_.X, SvgLengthUnits.Percentage), new SvgLength(p_.Y, SvgLengthUnits.Percentage))).ToArray();
  337. g.Transform = Matrix3x2.CreateTranslation(0, hedge * 0.7f);
  338. svg.Children.Clear();
  339. // Visible edges:
  340. svg.Children.Add(new SvgPolygonElement()
  341. {
  342. Points = new List<SvgPoint>()
  343. {
  344. svgPts[4],
  345. svgPts[1],
  346. svgPts[2],
  347. svgPts[3],
  348. svgPts[4],
  349. svgPts[2],
  350. },
  351. Stroke = pntStroke0,
  352. Fill = paintTrans,
  353. });
  354. // Bottom:
  355. svg.Children.Add(new SvgPolygonElement()
  356. {
  357. Points = svgPts.Take(4).ToList(),
  358. Fill = pntFill0,
  359. });
  360. // Invisible edges:
  361. svg.Children.Add(new SvgLineElement()
  362. {
  363. X1 = svgPts[0].X,
  364. Y1 = svgPts[0].Y,
  365. X2 = svgPts[4].X,
  366. Y2 = svgPts[4].Y,
  367. Stroke = pntStroke0inv,
  368. });
  369. svg.Children.Add(new SvgLineElement()
  370. {
  371. X1 = svgPts[0].X,
  372. Y1 = svgPts[0].Y,
  373. X2 = svgPts[1].X,
  374. Y2 = svgPts[1].Y,
  375. Stroke = pntStroke0inv,
  376. });
  377. svg.Children.Add(new SvgLineElement()
  378. {
  379. X1 = svgPts[0].X,
  380. Y1 = svgPts[0].Y,
  381. X2 = svgPts[3].X,
  382. Y2 = svgPts[3].Y,
  383. Stroke = pntStroke0inv,
  384. });
  385. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  386. g.Transform = Matrix3x2.Identity;
  387. DrawCaption(g, ip, grid.Radius, "Pyramid");
  388. ip.X = startIp.X;
  389. ip.Y += grid.StepY;
  390.  
  391. // Cone:
  392. float baseh = grid.Radius * 0.3f;
  393. svgPts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "Cone");
  394. svg.Children.Clear();
  395. svg.Children.Add(new SvgPolylineElement()
  396. {
  397. Points = new List<SvgPoint>()
  398. {
  399. svgPts[2], svgPts[0], svgPts[1],
  400. },
  401. Stroke = pntStroke0,
  402. Fill = paintTrans,
  403. });
  404. svg.Children.Add(new SvgEllipseElement()
  405. {
  406. CenterX = new SvgLength(svgPts[0].X.Value, SvgLengthUnits.Percentage),
  407. CenterY = svgPts[2].Y,
  408. RadiusX = new SvgLength((svgPts[1].X.Value - svgPts[2].X.Value) / 2, SvgLengthUnits.Percentage),
  409. RadiusY = len10,
  410. Fill = pntFill0,
  411. Stroke = pntStroke0,
  412. });
  413. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  414. ip.X += grid.StepX;
  415.  
  416. // Parallelogram (use graphics.Transform on a rectangle):
  417. svg.Children.Clear();
  418. var svgParal = new SvgRectElement()
  419. {
  420. X = new SvgLength(-20, SvgLengthUnits.Percentage),
  421. Y = len20,
  422. Width = new SvgLength(140, SvgLengthUnits.Percentage),
  423. Height = len60,
  424. Stroke = pntStroke0,
  425. Fill = paintTrans,
  426. };
  427. svg.Children.Add(svgParal);
  428. g.Transform = Matrix3x2.CreateSkew((float)Math.PI / 6, 0) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  429. g.DrawSvg(svgDoc, new RectangleF(-grid.Radius, -grid.Radius, grid.Radius * 2, grid.Radius * 2));
  430. g.Transform = Matrix3x2.Identity;
  431. DrawCaption(g, ip, grid.Radius, "Parallelogram");
  432. ip.X += grid.StepX;
  433.  
  434. // Trapezoid (use DrawPolygon to just get the points of the square):
  435. svg.Children.Clear();
  436. float dx = 10;
  437. svgPts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "Trapezoid");
  438. svg.Children.Add(new SvgPolygonElement()
  439. {
  440. Points = new List<SvgPoint>()
  441. {
  442. new SvgPoint(new SvgLength(svgPts[0].X.Value - dx, SvgLengthUnits.Percentage), svgPts[0].Y),
  443. new SvgPoint(new SvgLength(svgPts[1].X.Value + dx, SvgLengthUnits.Percentage), svgPts[1].Y),
  444. new SvgPoint(new SvgLength(svgPts[2].X.Value - dx, SvgLengthUnits.Percentage), svgPts[2].Y),
  445. new SvgPoint(new SvgLength(svgPts[3].X.Value + dx, SvgLengthUnits.Percentage), svgPts[3].Y),
  446. },
  447. Stroke = pntStroke0,
  448. Fill = paintTrans,
  449. });
  450. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  451.  
  452. // Done:
  453. doc.Save(stream);
  454. return doc.Pages.Count;
  455. }
  456. }
  457. }
  458.