SvgMergedShapes.vb
''
'' This code is part of Document Solutions for Imaging demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Collections.Generic
Imports System.Linq
Imports System.Numerics
Imports System.Xml
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging
Imports GrapeCity.Documents.Svg

'' 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 ReadOnly Property DefaultMime As String
        Get
            Return Util.MimeTypes.SVG
        End Get
    End Property

    Public Function GenerateImageStream(
            ByVal targetMime As String,
            ByVal pixelSize As Size,
            ByVal dpi As Single,
            ByVal opaque As Boolean,
            Optional ByVal sampleParams As String() = Nothing) As Stream

        If targetMime <> Util.MimeTypes.SVG Then
            Throw New Exception("This sample only supports SVG output format.")
        End If

        Using svgDoc As New GcSvgDocument()
            Dim svg = svgDoc.RootSvg
            Dim items = svg.Children

            Dim width = New SvgLength(pixelSize.Width)
            Dim height = New SvgLength(pixelSize.Height)

            svg.Width = width
            svg.Height = height

            '' Root title:
            Dim title = New SvgTitleElement()
            items.Add(title)
            title.Children.Add(New SvgContentElement(XmlNodeType.Text) With {
                .Content = "Root SVG Title"
            })

            '' Root bounds:
            Dim bounds = New SvgRectElement() With {
                .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:
            Dim centerX As Single = 200.0F
            Dim centerY As Single = 200.0F
            Dim radius As Single = 190.0F
            Dim polyWidth As Single = 180.0F
            Dim polyHeight As Single = 320.0F
            Dim polyD As Single = 30.0F
            Dim pad As Single = 10.0F

            '' We add an outer group to hold everything else,
            '' so that it can be easily positioned within the overall bounds:
            Dim outerGroup = New SvgGroupElement()
            items.Add(outerGroup)
            items = outerGroup.Children

            '' Center the group:
            Dim outerGroupTranslateX = New SvgLength(pixelSize.Width / 2.0F - centerX)
            Dim outerGroupTranslateY = New SvgLength(pixelSize.Height / 2.0F - centerY - polyHeight / 2.0F)
            outerGroup.Transform = New List(Of SvgTransform) From {
                New SvgTranslateTransform() With {.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:
            Dim 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:
            Dim clipPathOuter = New SvgClipPathElement() With {.ID = "outerClip"}
            defs.Children.Add(clipPathOuter)
            clipPathOuter.Children.Add(New SvgCircleElement() With {
                .CenterX = New SvgLength(centerX),
                .CenterY = New SvgLength(centerY),
                .Radius = New SvgLength(radius)
            })
            clipPathOuter.Children.Add(New SvgPolygonElement() With {
                .Points = New List(Of SvgPoint) From {
                    New SvgPoint(New SvgLength(centerX - polyWidth / 2.0F), New SvgLength(centerY + radius * 0.8F)),
                    New SvgPoint(New SvgLength(centerX - polyWidth / 2.0F - polyD), New SvgLength(centerY + radius + polyHeight)),
                    New SvgPoint(New SvgLength(centerX + polyWidth / 2.0F + polyD), New SvgLength(centerY + radius + polyHeight)),
                    New SvgPoint(New SvgLength(centerX + polyWidth / 2.0F), 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:
            Dim clipPathInner = New SvgClipPathElement() With {.ID = "innerClip"}
            defs.Children.Add(clipPathInner)
            clipPathInner.Children.Add(New SvgCircleElement() With {
                .CenterX = New SvgLength(centerX),
                .CenterY = New SvgLength(centerY),
                .Radius = New SvgLength(radius - pad)
            })
            clipPathInner.Children.Add(New SvgRectElement() With {
                .X = New SvgLength(centerX - polyWidth / 2.0F + pad),
                .Y = New SvgLength(centerY + radius * 0.8F),
                .Width = New SvgLength(polyWidth - pad * 2.0F),
                .Height = New SvgLength(polyHeight - pad * 2.0F)
            })

            '' A filled rectangle taking up the whole SVG area (100%x100%)
            '' but clipped by the "outerClip" figure:
            Dim rcOuter = New SvgRectElement() With {
                .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)
            Dim descR = New SvgTitleElement()
            descR.Children.Add(New SvgContentElement(XmlNodeType.Text) With {
                .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:
            Dim groupEl = New SvgGroupElement() With {
                .ClipPath = New SvgReference("innerClip")
            }
            items.Add(groupEl)

            '' We add to the group clipped by "innerClip"
            '' a filled rectangle taking up the whole area (100%x100%):
            Dim rcInner = New SvgRectElement() With {
                .Width = New SvgLength(width.Value - outerGroupTranslateX.Value),
                .Height = New SvgLength(height.Value - outerGroupTranslateY.Value),
                .Fill = New SvgPaint(Color.DarkOrange)
            }
            groupEl.Children.Add(rcInner)

            Dim desc = New SvgTitleElement()
            rcInner.Children.Add(desc)
            desc.Children.Add(New SvgContentElement(XmlNodeType.Text) With {
                .Content = "Title of the dark orange rectangle"
            })

            '' We also add to the group an image that is rotated
            '' and drawn semi-transparently:
            Dim imagePath = Path.Combine("Resources", "Images", "colosseum-resized.jpg")
            Dim bmp = New GcBitmap(imagePath)

            Dim imgTranslateX = New SvgLength(188)
            Dim imgTranslateY = New SvgLength(-55)

            Dim img = New SvgImageElement() With {
                .Href = New SvgReference(bmp, True) With {.InJpegFormat = True},
                .Width = New SvgLength(400),
                .Height = New SvgLength(525),
                .Transform = New List(Of SvgTransform) From {
                    New SvgTranslateTransform() With {.TranslateX = imgTranslateX, .TranslateY = imgTranslateY},
                    New SvgRotateTransform() With {.Angle = New SvgAngle(30)}
                },
                .Opacity = 0.8F
            }
            groupEl.Children.Add(img)

            Dim descImg = New SvgTitleElement()
            img.Children.Add(descImg)
            descImg.Children.Add(New SvgContentElement(XmlNodeType.Text) With {
                .Content = "Title of the image"
            })

            '' Save SVG to the output Stream
            Dim ms As New MemoryStream()
            svgDoc.Save(ms, New XmlWriterSettings() With {.Indent = True})
            ms.Seek(0, SeekOrigin.Begin)
            Return ms
        End Using
    End Function
End Class