SvgToGrayscale.cs
//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Svg;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;
using DsPdfWeb.Demos.Common;

namespace DsPdfWeb.Demos
{
    // This sample is similar to SvgClipArt, but after loading each SVG image
    // it converts all strokes and fills in it to grayscale.
    //
    // The SVG clip art used in this sample is from freesvg.org.
    public class SvgToGrayscale
    {
        void ToGrayscale(SvgElementCollection elements)
        {
            foreach (var el in elements)
            {
                if (el is SvgGraphicsElement elg)
                {
                    elg.Stroke = PaintToGrayscale(elg.Stroke);
                    elg.Fill = PaintToGrayscale(elg.Fill);
                }
                ToGrayscale(el.Children);
            }
        }

        // Simplified conversion of an SvgPaint to grayscale
        // (Y formula from https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/):
        SvgPaint PaintToGrayscale(SvgPaint src)
        {
            if (src == null)
                return null;
            else if (src.PaintType == SvgPaintType.Color)
            {
                var rgb = src.Color.Rgb;
                var Y = (int)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);
            }
        }

        public int CreatePDF(Stream stream)
        {
            // Load images from the resources folder:
            var images = new List<(string, GcSvgDocument)>();
            foreach (var fname in Directory.GetFiles(Path.Combine("Resources", "SvgClipArt"), "*", SearchOption.AllDirectories))
            {
                var svg = GcSvgDocument.FromFile(fname);
                ToGrayscale(svg.RootSvg.Children);
                images.Add((Path.GetFileName(fname), svg));
            }
            images.Shuffle();

            var doc = new GcPdfDocument();
            // Font and format for captions:
            const float sMargin = 72f / 6;
            var font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeui.ttf"));
            var tf = new TextFormat() { Font = font, FontSize = sMargin * 0.65f };

            // Set up a 3x4 layout grid with 1/2" margins all around:
            const float margin = 36;
            const int rows = 4;
            const int cols = 3;
            float gapx = 72f / 4, gapy = gapx;
            float sWidth = (doc.PageSize.Width - margin * 2 + gapx) / cols;
            float sHeight = (doc.PageSize.Height - margin * 2 + gapy) / rows;
            if (sWidth > sHeight)
            {
                gapx += sWidth - sHeight;
                sWidth = sHeight;
            }
            else
            {
                gapy += sHeight - sWidth;
                sHeight = sWidth;
            }
            var ip = new PointF(margin, margin);

            // Render all images within the grid, adding new pages as needed:
            var g = doc.NewPage().Graphics;
            for (int i = 0; i < images.Count(); ++i)
            {
                // Draw border around image:
                var 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:
                var svg = images[i].Item2;
                var s = svg.GetIntrinsicSize(SvgLengthUnits.Points);
                if (s.Width > 0 && s.Height > 0)
                {
                    // If image proportions are different from our target rectangle,
                    // we resize the rectangle centering the image in it:
                    var qSrc = s.Width / s.Height;
                    var qTgt = rect.Width / rect.Height;
                    if (qSrc < qTgt)
                        rect.Inflate(rect.Width * (qSrc / qTgt - 1) / 2, 0);
                    else if (qSrc > qTgt)
                        rect.Inflate(0, rect.Height * (qTgt / qSrc - 1) / 2);
                }
                // 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 && i < images.Count() - 1)
                {
                    ip.X = margin;
                    ip.Y += sHeight;
                    if (ip.Y + sHeight > doc.PageSize.Height)
                    {
                        g = doc.NewPage().Graphics;
                        ip.Y = margin;
                    }
                }
            }
            // Done:
            doc.Save(stream);
            // Dispose images after saving the PDF:
            images.ForEach(t_ => t_.Item2.Dispose());
            return doc.Pages.Count;
        }
    }
}