ImageArticles.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.Pdf.Articles;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Drawing;
using DsPdfWeb.Demos.Common;

namespace DsPdfWeb.Demos
{
    // This sample shows how to create article threads in a PDF document.
    // An article thread is a sequence of related pages or page areas that can be
    // navigated sequentially (forward or back) in a supporting PDF viewer.
    // In this sample we load a number of photos from a folder and render them
    // one per page in a random order.
    // Some photos are associated (via known file names) with a specific subject
    // (buildings, art, etc.), and we put all images associated with
    // each subject into a subject-specific article thread (images that are not
    // associated with any known subject are put in the 'Miscellaneous' thread).
    // In addition we create 3 threads for different aspect ratios (horizontal,
    // vertical and square), and add each image to the appropriate aspect article.
    // See section 'Article threads' in Navigating PDF pages
    // for details on how to navigate article threads in Acrobat
    // (our JavaScript PDF viewer provides a similar UI for this).
    public class ImageArticles
    {
        // Article names:
        static class ArticleNames
        {
            public static string Landscape => "Subject: landscape";
            public static string Art => "Subject: art";
            public static string Flora => "Subject: flora";
            public static string Buildings => "Subject: buildings";
            public static string Misc => "Subject: Miscellaneous";
            public static string AspectHorz => "Aspect: horizontal";
            public static string AspectVert => "Aspect: vertical";
            public static string AspectSquare => "Aspect: square";
        }

        // Associate known image file names with appropriate subjects:
        static readonly Dictionary<string, string> _subjects = new Dictionary<string, string>()
        {
            {"aurora.jpg", ArticleNames.Landscape },
            {"chairs.jpg", ArticleNames.Buildings },
            {"clouds.jpg", ArticleNames.Landscape },
            {"colosseum.jpg", ArticleNames.Art },
            {"deadwood.jpg", ArticleNames.Flora },
            {"door.jpg", ArticleNames.Buildings },
            {"ferns.jpg", ArticleNames.Flora },
            {"fiord.jpg", ArticleNames.Landscape },
            {"firth.jpg", ArticleNames.Landscape },
            {"lady.jpg", ArticleNames.Art },
            {"lavender.jpg", ArticleNames.Flora },
            {"maple.jpg", ArticleNames.Buildings },
            {"minerva.jpg", ArticleNames.Art },
            {"newfoundland.jpg", ArticleNames.Landscape },
            {"pines.jpg", ArticleNames.Flora },
            {"purples.jpg", ArticleNames.Flora },
            {"reds.jpg", ArticleNames.Flora },
            {"road.jpg", ArticleNames.Landscape },
            {"rome.jpg", ArticleNames.Art },
            {"roofs.jpg", ArticleNames.Buildings },
            {"sea.jpg", ArticleNames.Landscape },
            {"skye.jpg", ArticleNames.Landscape },
            {"tudor.jpg", ArticleNames.Buildings },
            {"windswept.jpg", ArticleNames.Flora },
            // Images not in this list are 'misc'.
        };

        // Class to hold image info:
        private class ImageInfo
        {
            public string Name;
            public IImage Image;
            public string Subject;
            public string Aspect;
        }

        public int CreatePDF(Stream stream)
        {
            // Load images and their associated infos:
            var imageInfos = new List<ImageInfo>();
            foreach (var fname in Directory.GetFiles(Path.Combine("Resources", "Images"), "*", SearchOption.AllDirectories))
            {
                var image = Util.ImageFromFile(fname);
                var aspect = image.Width > image.Height ? ArticleNames.AspectHorz :
                    (image.Width < image.Height ? ArticleNames.AspectVert : ArticleNames.AspectSquare);
                var name = Path.GetFileName(fname);
                _subjects.TryGetValue(name, out string subject);
                if (string.IsNullOrEmpty(subject))
                    subject = ArticleNames.Misc;
                imageInfos.Add(new ImageInfo()
                {
                    Name = name,
                    Image = image,
                    Subject = subject,
                    Aspect = aspect
                });
            }
            // Randomize the order of images in the PDF:
            imageInfos.Shuffle();

            // Keys are article thread names (from ArticleNames),
            // values are ArticleThread objects to be added to the PDF:
            var articles = new Dictionary<string, ArticleThread>();
            foreach (var subject in _subjects.Values.Distinct())
                articles.Add(subject,
                    new ArticleThread()
                    {
                        Info = new DocumentInfo() { Title = subject }
                    });
            articles.Add(ArticleNames.Misc,
                new ArticleThread()
                {
                    Info = new DocumentInfo() { Title = ArticleNames.Misc }
                });
            var horizontals = new ArticleThread()
            {
                Info = new DocumentInfo() { Title = ArticleNames.AspectHorz }
            };
            var verticals = new ArticleThread()
            {
                Info = new DocumentInfo() { Title = ArticleNames.AspectVert }
            };
            var squares = new ArticleThread()
            {
                Info = new DocumentInfo() { Title = ArticleNames.AspectSquare }
            };

            // Create the document:
            var doc = new GcPdfDocument();

            // Add images (one per page) to the PDF and article threads:
            var ia = new ImageAlign(ImageAlignHorz.Center, ImageAlignVert.Top, true, true, true, false, false);
            for (int i = 0; i < imageInfos.Count; ++i)
            {
                var page = doc.NewPage();
                var ii = imageInfos[i];
                var rc = new RectangleF(72, 72, doc.PageSize.Width - 144, doc.PageSize.Height - 144);
                // Note that we get the actual image bounds to precisely specify the page area in the thread:
                page.Graphics.DrawImage(ii.Image, rc, null, ia, out RectangleF[] imageBounds);
                var bounds = imageBounds[0];
                // Add the image to proper subject and aspect threads:
                articles[ii.Subject].Beads.Add(new ArticleBead() { Page = page, Bounds = bounds });
                if (ii.Aspect == ArticleNames.AspectHorz)
                    horizontals.Beads.Add(new ArticleBead() { Page = page, Bounds = bounds });
                else if (ii.Aspect == ArticleNames.AspectVert)
                    verticals.Beads.Add(new ArticleBead() { Page = page, Bounds = bounds });
                else
                    squares.Beads.Add(new ArticleBead() { Page = page, Bounds = bounds });
            }
            // Add subject and aspect article threads to the PDF:
            foreach (var article in articles.Select(a_ => a_.Value))
                doc.ArticleThreads.Add(article);
            doc.ArticleThreads.Add(horizontals);
            doc.ArticleThreads.Add(verticals);
            doc.ArticleThreads.Add(squares);

            // Done:
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}