SvgMergedShapes.cs
  1. //
  2. // This code is part of Document Solutions for Imaging demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Drawing;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Numerics;
  11. using GrapeCity.Documents.Drawing;
  12. using GrapeCity.Documents.Text;
  13. using GrapeCity.Documents.Imaging;
  14. using GrapeCity.Documents.Svg;
  15. using DsImagingWeb.Demos.Common;
  16. using System.Xml;
  17.  
  18. namespace DsImagingWeb.Demos
  19. {
  20. // This example shows how to combine SVG elements to create clipping paths
  21. // that are used to effectively merge geometric figures.
  22. // Parts of the image are associated with SVG title elements which show
  23. // as tooltips if the SVG is displayed in a browser.
  24. public class SvgMergedShapes
  25. {
  26. public string DefaultMime { get => Common.Util.MimeTypes.SVG; }
  27.  
  28. public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
  29. {
  30. if (targetMime != Common.Util.MimeTypes.SVG)
  31. throw new Exception("This sample only supports SVG output format.");
  32.  
  33. using var svgDoc = new GcSvgDocument();
  34. var svg = svgDoc.RootSvg;
  35. var items = svg.Children;
  36.  
  37. var width = new SvgLength(pixelSize.Width);
  38. var height = new SvgLength(pixelSize.Height);
  39.  
  40. svg.Width = width;
  41. svg.Height = height;
  42.  
  43. // Root title:
  44. var title = new SvgTitleElement();
  45. items.Add(title);
  46. title.Children.Add(new SvgContentElement(XmlNodeType.Text)
  47. {
  48. Content = "Root SVG Title"
  49. });
  50. // Root bounds:
  51. var bounds = new SvgRectElement()
  52. {
  53. X = new SvgLength(0),
  54. Y = new SvgLength(0),
  55. Width = width,
  56. Height = height,
  57. Stroke = new SvgPaint(new SvgColor(Color.DarkBlue)),
  58. Fill = new SvgPaint(new SvgColor(Color.DarkSeaGreen)),
  59. };
  60. items.Add(bounds);
  61.  
  62. // Geometry:
  63. float
  64. centerX = 200,
  65. centerY = 200,
  66. radius = 190,
  67. polyWidth = 180,
  68. polyHeight = 320,
  69. polyD = 30,
  70. pad = 10;
  71.  
  72. // We add an outer group to hold everything else,
  73. // so that it can be easily positioned within the overall bounds:
  74. var outerGroup = new SvgGroupElement();
  75. items.Add(outerGroup);
  76. items = outerGroup.Children;
  77. // Center the group:
  78. var outerGroupTranslateX = new SvgLength(pixelSize.Width / 2 - centerX);
  79. var outerGroupTranslateY = new SvgLength(pixelSize.Height / 2 - centerY - polyHeight / 2);
  80. outerGroup.Transform = new List<SvgTransform>()
  81. {
  82. new SvgTranslateTransform() { TranslateX = outerGroupTranslateX, TranslateY = outerGroupTranslateY },
  83. };
  84.  
  85. // Add a 'defs' element containing clipPath elements
  86. // that will be used to merge various geometric figures
  87. // to provide a complex clipping path:
  88. var defs = new SvgDefsElement();
  89. items.Add(defs);
  90.  
  91. // Create the outer clip built by adding a circle and a polygon
  92. // resembling an inverted keystone, so that the union of the two figures
  93. // looks like a keyhole:
  94. var clipPathOuter = new SvgClipPathElement() { ID = "outerClip" };
  95. defs.Children.Add(clipPathOuter);
  96. clipPathOuter.Children.Add(new SvgCircleElement()
  97. {
  98. CenterX = new SvgLength(centerX),
  99. CenterY = new SvgLength(centerY),
  100. Radius = new SvgLength(radius)
  101. });
  102. clipPathOuter.Children.Add(new SvgPolygonElement()
  103. {
  104. Points = new List<SvgPoint>()
  105. {
  106. new SvgPoint(new SvgLength(centerX - polyWidth / 2), new SvgLength(centerY + radius * 0.8f)),
  107. new SvgPoint(new SvgLength(centerX - polyWidth / 2 - polyD), new SvgLength(centerY + radius + polyHeight)),
  108. new SvgPoint(new SvgLength(centerX + polyWidth / 2 + polyD), new SvgLength(centerY + radius + polyHeight)),
  109. new SvgPoint(new SvgLength(centerX + polyWidth / 2), new SvgLength(centerY + radius * 0.8f)),
  110. }
  111. });
  112.  
  113. // Create the inner clip by adding a smaller circle and a rectangle
  114. // that is smaller than the keystone polygon:
  115. var clipPathInner = new SvgClipPathElement() { ID = "innerClip" };
  116. defs.Children.Add(clipPathInner);
  117. clipPathInner.Children.Add(new SvgCircleElement()
  118. {
  119. CenterX = new SvgLength(centerX),
  120. CenterY = new SvgLength(centerY),
  121. Radius = new SvgLength(radius - pad)
  122. });
  123. clipPathInner.Children.Add(new SvgRectElement()
  124. {
  125. X = new SvgLength(centerX - polyWidth / 2 + pad),
  126. Y = new SvgLength(centerY + radius * 0.8f),
  127. Width = new SvgLength(polyWidth - pad * 2),
  128. Height = new SvgLength(polyHeight - pad * 2)
  129. });
  130.  
  131. // A filled rectangle taking up the whole SVG area (100%x100%)
  132. // but clipped by the "outerClip" figure:
  133. var rcOuter = new SvgRectElement()
  134. {
  135. Width = new SvgLength(width.Value - outerGroupTranslateX.Value),
  136. Height = new SvgLength(height.Value - outerGroupTranslateY.Value),
  137. Fill = new SvgPaint(Color.DarkBlue),
  138. ClipPath = new SvgReference("outerClip")
  139. };
  140. items.Add(rcOuter);
  141. var descR = new SvgTitleElement();
  142. descR.Children.Add(new SvgContentElement(XmlNodeType.Text)
  143. {
  144. Content = "Title of the dark blue rectangle"
  145. });
  146. rcOuter.Children.Add(descR);
  147.  
  148. // Group clipped by the "innerClip", this will contain
  149. // a yellow fill and a rotated semi-transparent raster image:
  150. var group = new SvgGroupElement()
  151. {
  152. ClipPath = new SvgReference("innerClip")
  153. };
  154. items.Add(group);
  155.  
  156. // We add to the group clipped by "innerClip"
  157. // a filled rectangle taking up the whole area (100%x100%):
  158. var rcInner = new SvgRectElement()
  159. {
  160. Width = new SvgLength(width.Value - outerGroupTranslateX.Value),
  161. Height = new SvgLength(height.Value - outerGroupTranslateY.Value),
  162. Fill = new SvgPaint(Color.DarkOrange),
  163. };
  164. group.Children.Add(rcInner);
  165.  
  166. var desc = new SvgTitleElement();
  167. rcInner.Children.Add(desc);
  168. desc.Children.Add(new SvgContentElement(XmlNodeType.Text)
  169. {
  170. Content = "Title of the dark orange rectangle"
  171. });
  172.  
  173. // We also add to the group an image that is rotated
  174. // and drawn semi-transparently:
  175. var imagePath = Path.Combine("Resources", "Images", "colosseum-resized.jpg");
  176. var bmp = new GcBitmap(imagePath);
  177.  
  178. var imgTranslateX = new SvgLength(188);
  179. var imgTranslateY = new SvgLength(-55);
  180.  
  181. var img = new SvgImageElement()
  182. {
  183. Href = new SvgReference(bmp, true) { InJpegFormat = true },
  184. Width = new SvgLength(400),
  185. Height = new SvgLength(525),
  186. Transform = new List<SvgTransform>()
  187. {
  188. new SvgTranslateTransform() { TranslateX = imgTranslateX, TranslateY = imgTranslateY },
  189. new SvgRotateTransform() { Angle = new SvgAngle(30) }
  190. },
  191. Opacity = 0.8f,
  192. };
  193. group.Children.Add(img);
  194.  
  195. var descImg = new SvgTitleElement();
  196. img.Children.Add(descImg);
  197. descImg.Children.Add(new SvgContentElement(XmlNodeType.Text)
  198. {
  199. Content = "Title of the image"
  200. });
  201.  
  202. // Save SVG to the output Stream
  203. var ms = new MemoryStream();
  204. svgDoc.Save(ms, new XmlWriterSettings() { Indent = true });
  205. ms.Seek(0, SeekOrigin.Begin);
  206. return ms;
  207. }
  208. }
  209. }
  210.