SvgToGrayscale.vb
''
'' This code is part of Document Solutions for PDF .NET demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Linq
Imports System.Collections.Generic
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Svg
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' This sample is similar to SvgClipArt, but after loading each SVG image
'' it converts all strokes and fills in it to grayscale.
Public Class SvgToGrayscale
    Sub ToGrayscale(ByVal elements As SvgElementCollection)
        For Each el In elements
            If TypeOf el Is SvgGraphicsElement Then
                Dim elg = DirectCast(el, SvgGraphicsElement)
                elg.Stroke = PaintToGrayscale(elg.Stroke)
                elg.Fill = PaintToGrayscale(elg.Fill)
            End If
            ToGrayscale(el.Children)
        Next
    End Sub

    '' Simplified conversion of an SvgPaint to grayscale
    '' (Y formula from https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/):
    Function PaintToGrayscale(ByVal src As SvgPaint) As SvgPaint
        If src Is Nothing Then
            Return Nothing
        ElseIf src.PaintType = SvgPaintType.Color Then
            Dim rgb = src.Color.Rgb
            Dim Y = CInt(Math.Round(0.299 * rgb.R + 0.587 * rgb.G + 0.114 * rgb.B))
            Return New SvgPaint(Color.FromArgb(Y, Y, Y))
        Else
            Return New SvgPaint(Color.Gray)
        End If
    End Function

    Function CreatePDF(ByVal stream As Stream) As Integer
        '' Load images from the resources folder:
        Dim images = New List(Of (String, GcSvgDocument))()
        For Each fname In Directory.GetFiles(Path.Combine("Resources", "SvgClipArt"), "*", SearchOption.AllDirectories)
            Dim svg = GcSvgDocument.FromFile(fname)
            ToGrayscale(svg.RootSvg.Children)
            images.Add((Path.GetFileName(fname), svg))
        Next
        images.Shuffle()

        Dim doc = New GcPdfDocument()
        '' Font and format for captions:
        Const sMargin As Single = 72.0F / 6
        Dim font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSans-Regular.ttf"))
        Dim tf = New TextFormat() With {.Font = font, .FontSize = sMargin * 0.65F}

        '' Set up a 3x4 layout grid with 1/2" margins all around:
        Const margin As Single = 36
        Const rows As Integer = 4
        Const cols As Integer = 3
        Dim gapx As Single = 72.0F / 4
        Dim gapy As Single = gapx
        Dim sWidth As Single = (doc.PageSize.Width - margin * 2 + gapx) / cols
        Dim sHeight As Single = (doc.PageSize.Height - margin * 2 + gapy) / rows
        If sWidth > sHeight Then
            gapx += sWidth - sHeight
            sWidth = sHeight
        Else
            gapy += sHeight - sWidth
            sHeight = sWidth
        End If
        Dim ip = New PointF(margin, margin)

        '' Render all images within the grid, adding new pages as needed:
        Dim g = doc.NewPage().Graphics
        For i = 0 To images.Count - 1
            '' Draw border around image:
            Dim rect = New RectangleF(ip, New SizeF(sWidth - gapx, sHeight - gapy))
            g.FillRectangle(rect, Color.LightGray)
            g.DrawRectangle(rect, Color.Black, 0.5F)
            rect.Inflate(-sMargin, -sMargin)

            '' Draw the SVG:
            Dim svg = images(i).Item2
            Dim s = svg.GetIntrinsicSize(SvgLengthUnits.Points)
            If s.Width > 0 AndAlso s.Height > 0 Then
                '' If image proportions are different from our target rectangle,
                '' we resize the rectangle centering the image in it:
                Dim qSrc = s.Width / s.Height
                Dim qTgt = rect.Width / rect.Height
                If qSrc < qTgt Then
                    rect.Inflate(rect.Width * (qSrc / qTgt - 1) / 2, 0)
                ElseIf qSrc > qTgt Then
                    rect.Inflate(0, rect.Height * (qTgt / qSrc - 1) / 2)
                End If
            End If
            '' Render the SVG:
            g.DrawSvg(svg, rect)

            '' Print image file name as caption in the bottom slide margin:
            g.DrawString(Path.GetFileName(images(i).Item1), tf,
                New RectangleF(rect.X, rect.Bottom, rect.Width, sMargin),
                TextAlignment.Center, ParagraphAlignment.Near, False)
            ip.X += sWidth
            If ip.X + sWidth > doc.PageSize.Width AndAlso i < images.Count - 1 Then
                ip.X = margin
                ip.Y += sHeight
                If ip.Y + sHeight > doc.PageSize.Height Then
                    g = doc.NewPage().Graphics
                    ip.Y = margin
                End If
            End If
        Next

        '' Done:
        doc.Save(stream)
        '' Dispose images after saving the PDF:
        images.ForEach(Sub(t_) t_.Item2.Dispose())
        Return doc.Pages.Count
    End Function
End Class