//// This code is part of Document Solutions for Imaging demos.// Copyright (c) MESCIUS inc. All rights reserved.//usingSystem;usingSystem.IO;usingSystem.Drawing;usingSystem.Collections.Generic;usingGrapeCity.Documents.Imaging;usingGrapeCity.Documents.Svg;usingSystem.Xml; namespaceDsImagingWeb.Demos{// This example shows how to combine SVG elements to create clipping paths// that are used to effectively merge geometric figures.// Parts of the image are associated with SVG title elements which show// as tooltips if the SVG is displayed in a browser.publicclassSvgMergedShapes {publicstringDefaultMime { get => Common.Util.MimeTypes.SVG; } publicStreamGenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null) {if (targetMime != Common.Util.MimeTypes.SVG)thrownewException("This sample only supports SVG output format."); usingvar svgDoc = newGcSvgDocument();var svg = svgDoc.RootSvg;var items = svg.Children; var width = newSvgLength(pixelSize.Width);var height = newSvgLength(pixelSize.Height); svg.Width = width; svg.Height = height; // Root title:var title = newSvgTitleElement(); items.Add(title); title.Children.Add(newSvgContentElement(XmlNodeType.Text) {Content = "Root SVG Title" });// Root bounds:var bounds = newSvgRectElement() {X = newSvgLength(0),Y = newSvgLength(0),Width = width,Height = height,Stroke = newSvgPaint(newSvgColor(Color.DarkBlue)),Fill = newSvgPaint(newSvgColor(Color.DarkSeaGreen)), }; items.Add(bounds); // Geometry:float centerX = 200, centerY = 200, radius = 190, polyWidth = 180, polyHeight = 320, polyD = 30, pad = 10; // We add an outer group to hold everything else,// so that it can be easily positioned within the overall bounds:var outerGroup = newSvgGroupElement(); items.Add(outerGroup); items = outerGroup.Children;// Center the group:var outerGroupTranslateX = newSvgLength(pixelSize.Width / 2 - centerX);var outerGroupTranslateY = newSvgLength(pixelSize.Height / 2 - centerY - polyHeight / 2); outerGroup.Transform = newList<SvgTransform>() {newSvgTranslateTransform() { TranslateX = outerGroupTranslateX, TranslateY = outerGroupTranslateY }, }; // Add a 'defs' element containing clipPath elements// that will be used to merge various geometric figures// to provide a complex clipping path:var defs = newSvgDefsElement(); items.Add(defs); // Create the outer clip built by adding a circle and a polygon// resembling an inverted keystone, so that the union of the two figures// looks like a keyhole:var clipPathOuter = newSvgClipPathElement() { ID = "outerClip" }; defs.Children.Add(clipPathOuter); clipPathOuter.Children.Add(newSvgCircleElement() {CenterX = newSvgLength(centerX),CenterY = newSvgLength(centerY),Radius = newSvgLength(radius) }); clipPathOuter.Children.Add(newSvgPolygonElement() {Points = newList<SvgPoint>() {newSvgPoint(newSvgLength(centerX - polyWidth / 2), newSvgLength(centerY + radius * 0.8f)),newSvgPoint(newSvgLength(centerX - polyWidth / 2 - polyD), newSvgLength(centerY + radius + polyHeight)),newSvgPoint(newSvgLength(centerX + polyWidth / 2 + polyD), newSvgLength(centerY + radius + polyHeight)),newSvgPoint(newSvgLength(centerX + polyWidth / 2), newSvgLength(centerY + radius * 0.8f)), } }); // Create the inner clip by adding a smaller circle and a rectangle// that is smaller than the keystone polygon:var clipPathInner = newSvgClipPathElement() { ID = "innerClip" }; defs.Children.Add(clipPathInner); clipPathInner.Children.Add(newSvgCircleElement() {CenterX = newSvgLength(centerX),CenterY = newSvgLength(centerY),Radius = newSvgLength(radius - pad) }); clipPathInner.Children.Add(newSvgRectElement() {X = newSvgLength(centerX - polyWidth / 2 + pad),Y = newSvgLength(centerY + radius * 0.8f),Width = newSvgLength(polyWidth - pad * 2),Height = newSvgLength(polyHeight - pad * 2) }); // A filled rectangle taking up the whole SVG area (100%x100%)// but clipped by the "outerClip" figure:var rcOuter = newSvgRectElement() {Width = newSvgLength(width.Value - outerGroupTranslateX.Value),Height = newSvgLength(height.Value - outerGroupTranslateY.Value),Fill = newSvgPaint(Color.DarkBlue),ClipPath = newSvgReference("outerClip") }; items.Add(rcOuter);var descR = newSvgTitleElement(); descR.Children.Add(newSvgContentElement(XmlNodeType.Text) {Content = "Title of the dark blue rectangle" }); rcOuter.Children.Add(descR); // Group clipped by the "innerClip", this will contain// a yellow fill and a rotated semi-transparent raster image:var group = newSvgGroupElement() {ClipPath = newSvgReference("innerClip") }; items.Add(group); // We add to the group clipped by "innerClip"// a filled rectangle taking up the whole area (100%x100%):var rcInner = newSvgRectElement() {Width = newSvgLength(width.Value - outerGroupTranslateX.Value),Height = newSvgLength(height.Value - outerGroupTranslateY.Value),Fill = newSvgPaint(Color.DarkOrange), }; group.Children.Add(rcInner); var desc = newSvgTitleElement(); rcInner.Children.Add(desc); desc.Children.Add(newSvgContentElement(XmlNodeType.Text) {Content = "Title of the dark orange rectangle" }); // We also add to the group an image that is rotated// and drawn semi-transparently:var imagePath = Path.Combine("Resources", "Images", "colosseum-resized.jpg");var bmp = newGcBitmap(imagePath); var imgTranslateX = newSvgLength(188);var imgTranslateY = newSvgLength(-55); var img = newSvgImageElement() {Href = newSvgReference(bmp, true) { InJpegFormat = true },Width = newSvgLength(400),Height = newSvgLength(525),Transform = newList<SvgTransform>() {newSvgTranslateTransform() { TranslateX = imgTranslateX, TranslateY = imgTranslateY },newSvgRotateTransform() { Angle = newSvgAngle(30) } },Opacity = 0.8f, }; group.Children.Add(img); var descImg = newSvgTitleElement(); img.Children.Add(descImg); descImg.Children.Add(newSvgContentElement(XmlNodeType.Text) {Content = "Title of the image" }); // Save SVG to the output Streamvar ms = newMemoryStream(); svgDoc.Save(ms, newXmlWriterSettings() { Indent = true }); ms.Seek(0, SeekOrigin.Begin);return ms; } }}