SvgMergedShapes.cs
C# VB
//// This code is part of Document Solutions for Imaging demos.// Copyright (c) MESCIUS inc. All rights reserved.//using System;using System.IO;using System.Drawing;using System.Collections.Generic;using GrapeCity.Documents.Imaging;using GrapeCity.Documents.Svg;using System.Xml;
namespace DsImagingWeb.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. public class SvgMergedShapes { public string DefaultMime { get => Common.Util.MimeTypes.SVG; }
public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null) { if (targetMime != Common.Util.MimeTypes.SVG) throw new Exception("This sample only supports SVG output format.");
using var svgDoc = new GcSvgDocument(); var svg = svgDoc.RootSvg; var items = svg.Children;
var width = new SvgLength(pixelSize.Width); var height = new SvgLength(pixelSize.Height);
svg.Width = width; svg.Height = height;
// Root title: var title = new SvgTitleElement(); items.Add(title); title.Children.Add(new SvgContentElement(XmlNodeType.Text) { Content = "Root SVG Title" }); // Root bounds: var bounds = new SvgRectElement() { X = new SvgLength(0), Y = new SvgLength(0), Width = width, Height = height, Stroke = new SvgPaint(new SvgColor(Color.DarkBlue)), Fill = new SvgPaint(new SvgColor(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 = new SvgGroupElement(); items.Add(outerGroup); items = outerGroup.Children; // Center the group: var outerGroupTranslateX = new SvgLength(pixelSize.Width / 2 - centerX); var outerGroupTranslateY = new SvgLength(pixelSize.Height / 2 - centerY - polyHeight / 2); outerGroup.Transform = new List<SvgTransform>() { new SvgTranslateTransform() { 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 = new SvgDefsElement(); 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 = new SvgClipPathElement() { ID = "outerClip" }; defs.Children.Add(clipPathOuter); clipPathOuter.Children.Add(new SvgCircleElement() { CenterX = new SvgLength(centerX), CenterY = new SvgLength(centerY), Radius = new SvgLength(radius) }); clipPathOuter.Children.Add(new SvgPolygonElement() { Points = new List<SvgPoint>() { new SvgPoint(new SvgLength(centerX - polyWidth / 2), new SvgLength(centerY + radius * 0.8f)), new SvgPoint(new SvgLength(centerX - polyWidth / 2 - polyD), new SvgLength(centerY + radius + polyHeight)), new SvgPoint(new SvgLength(centerX + polyWidth / 2 + polyD), new SvgLength(centerY + radius + polyHeight)), new SvgPoint(new SvgLength(centerX + polyWidth / 2), new SvgLength(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 = new SvgClipPathElement() { ID = "innerClip" }; defs.Children.Add(clipPathInner); clipPathInner.Children.Add(new SvgCircleElement() { CenterX = new SvgLength(centerX), CenterY = new SvgLength(centerY), Radius = new SvgLength(radius - pad) }); clipPathInner.Children.Add(new SvgRectElement() { X = new SvgLength(centerX - polyWidth / 2 + pad), Y = new SvgLength(centerY + radius * 0.8f), Width = new SvgLength(polyWidth - pad * 2), Height = new SvgLength(polyHeight - pad * 2) });
// A filled rectangle taking up the whole SVG area (100%x100%) // but clipped by the "outerClip" figure: var rcOuter = new SvgRectElement() { Width = new SvgLength(width.Value - outerGroupTranslateX.Value), Height = new SvgLength(height.Value - outerGroupTranslateY.Value), Fill = new SvgPaint(Color.DarkBlue), ClipPath = new SvgReference("outerClip") }; items.Add(rcOuter); var descR = new SvgTitleElement(); descR.Children.Add(new SvgContentElement(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 = new SvgGroupElement() { ClipPath = new SvgReference("innerClip") }; items.Add(group);
// We add to the group clipped by "innerClip" // a filled rectangle taking up the whole area (100%x100%): var rcInner = new SvgRectElement() { Width = new SvgLength(width.Value - outerGroupTranslateX.Value), Height = new SvgLength(height.Value - outerGroupTranslateY.Value), Fill = new SvgPaint(Color.DarkOrange), }; group.Children.Add(rcInner);
var desc = new SvgTitleElement(); rcInner.Children.Add(desc); desc.Children.Add(new SvgContentElement(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 = new GcBitmap(imagePath);
var imgTranslateX = new SvgLength(188); var imgTranslateY = new SvgLength(-55);
var img = new SvgImageElement() { Href = new SvgReference(bmp, true) { InJpegFormat = true }, Width = new SvgLength(400), Height = new SvgLength(525), Transform = new List<SvgTransform>() { new SvgTranslateTransform() { TranslateX = imgTranslateX, TranslateY = imgTranslateY }, new SvgRotateTransform() { Angle = new SvgAngle(30) } }, Opacity = 0.8f, }; group.Children.Add(img);
var descImg = new SvgTitleElement(); img.Children.Add(descImg); descImg.Children.Add(new SvgContentElement(XmlNodeType.Text) { Content = "Title of the image" });
// Save SVG to the output Stream var ms = new MemoryStream(); svgDoc.Save(ms, new XmlWriterSettings() { Indent = true }); ms.Seek(0, SeekOrigin.Begin); return ms; } }}