ImageArticles.cs
// 
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © 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
{
    // このサンプルは、PDF 文書に記事のスレッドを作成する方法を示します。
    // 記事のスレッドは、サポートしている PDF ビューアで順方向(前後)に移動できる
    // 一連の関連ページまたはページ領域です。
    // このサンプルでは、フォルダから多数の写真を読み込み、それらを1ページに1つずつ
    // ランダムな順序で描画します。
    // いくつかの写真は特定の主題(建物、アートなど)に(既知のファイル名で)関連付け
    // られており、各主題に関連するすべての画像を主題固有の記事スレッドに入れます
    // (既知の主題に関連付けられていない画像は 「その他」のスレッドに入れます。
    // さらに、縦横比(水平、垂直、正方形)ごとに3つのスレッドを作成し、各画像を適切な
    // アスペクト記事に追加します。
    // Acrobat で記事のスレッドをナビゲートする方法の詳細については、Navigating PDF pages の
    // 「Article threads」セクションを参照してください。
    // (本デモサイト内の PDF ビューアは、これに似た UI を提供します)。
    public class ImageArticles
    {
        // 記事の名前です。
        static class ArticleNames
        {
            public static string Landscape => "件名:風景";
            public static string Art => "件名:アート";
            public static string Flora => "件名:植物";
            public static string Buildings => "件名:建物";
            public static string Misc => "件名:その他";
            public static string AspectHorz => "アスペクト:水平";
            public static string AspectVert => "アスペクト:垂直";
            public static string AspectSquare => "アスペクト:正方形";
        }

        // 既知の画像ファイル名を適切な主題に関連付けます。
        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 },
            // このリストにない画像は 'misc'として扱われます。
        };

        // 画像情報を保持するクラス
        private class ImageInfo
        {
            public string Name;
            public IImage Image;
            public string Subject;
            public string Aspect;
        }

        public int CreatePDF(Stream stream)
        {
            // 画像とその関連情報を読み込みます。
            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
                });
            }
            // PDF内の画像をランダムに並べ替えます。
            imageInfos.Shuffle();

            // キーは(ArticleNames からの)記事のスレッド名、
            // 値は PDF に追加される ArticleThread オブジェクトです。
            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 }
            };

            // ドキュメントを生成します。
            var doc = new GcPdfDocument();

            // PDF と記事のスレッドに画像を(ページごとに1つ)追加します。
            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);
                // スレッド内のページ領域を正確に指定するために実際の画像境界を取得します。
                page.Graphics.DrawImage(ii.Image, rc, null, ia, out RectangleF[] imageBounds);
                var bounds = imageBounds[0];
                // 適切な件名とアスペクトのスレッドに画像を追加します。
                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 });
            }
            // 件名とアスペクトの記事のスレッドを 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);

            // PDF ドキュメントを保存します。
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}