ImageLinks.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.Text;
using GrapeCity.Documents.Drawing;
using DsPdfWeb.Demos.Common;
using GrapeCity.Documents.Pdf.Annotations;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos
{
    // このサンプルは、ディレクトリ内にあるすべての画像を読み込み、
    // 各画像を PDF の各ページにできるだけ大きなサイズでレンダリングします。
    // 最後に、大きな画像にリンクされた画像サムネイルの目次を文書に挿入します。
    // SlidePages も参照してください。
    public class ImageLinks
    {
        private class ImageInfo
        {
            public string Name;
            public IImage Image;
            public int PageIdx;
        }

        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeui.ttf"));
            // 周囲に1/4 インチの余白を設定します。
            const float margin = 36;

            // Resources/Images フォルダからすべての画像を読み込みます。
            List<ImageInfo> imageInfos = new List<ImageInfo>();
            foreach (var fname in Directory.GetFiles(Path.Combine("Resources", "Images"), "*", SearchOption.AllDirectories))
                imageInfos.Add(new ImageInfo() { Name = Path.GetFileName(fname), Image = Util.ImageFromFile(fname) });
            imageInfos.Shuffle();
            // 画像を水平方向の中央および垂直方向の上部に配置するよう設定します。
            var ia = new ImageAlign(ImageAlignHorz.Center, ImageAlignVert.Top, true, true, true, false, false);
            // ページ全体に表示するためのフルサイズの画像矩形。
            var rBig = new RectangleF(margin, margin, doc.PageSize.Width - margin * 2, doc.PageSize.Height - margin * 2);
            // 各ページに1つずつフルサイズの画像を描画します。
            for (int i = 0; i < imageInfos.Count; ++i)
            {
                var g = doc.NewPage().Graphics;
                var ii = imageInfos[i];
                g.DrawImage(ii.Image, rBig, null, ia);
                ii.PageIdx = i;
            }
            // サムネイル付きのページをドキュメントの先頭に 4x5 グリッドとして挿入します(SlidePages を参照してください)
            const int rows = 5;
            const int cols = 4;
            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;
            }
            const float sMargin = 72f / 6;
            // サムネイルを垂直方向の中央に配置します。
            ia.AlignVert = ImageAlignVert.Center;
            // 画像キャプションのテキスト書式。
            var tf = new TextFormat() { Font = font, FontSize = sMargin * 0.65f };
            // 挿入位置。
            var ip = new PointF(margin, margin);
            var page = doc.Pages.Insert(0);
            for (int i = 0; i < imageInfos.Count(); ++i)
            {
                var ii = imageInfos[i];
                var rect = new RectangleF(ip, new SizeF(sWidth - gapx, sHeight - gapy));
                // フルサイズの画像があるページへのリンクを追加します。
                // (目次に含まれるページの数がわかると、ページインデックスが更新されます)
                page.Annotations.Add(new LinkAnnotation(rect, new DestinationFit(ii.PageIdx)));
                // サムネイルを描画します。
                var g = page.Graphics;
                g.FillRectangle(rect, Color.LightGray);
                g.DrawRectangle(rect, Color.Black, 0.5f);
                rect.Inflate(-sMargin, -sMargin);
                g.DrawImage(ii.Image, rect, null, ia, out RectangleF[] imageRect);
                g.DrawRectangle(imageRect[0], Color.DarkGray, 1);
                // スライドの下部余白に画像ファイル名をキャプションとして出力します。
                g.DrawString(ii.Name, 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)
                {
                    ip.X = margin;
                    ip.Y += sHeight;
                    if (ip.Y + sHeight > doc.PageSize.Height)
                    {
                        page = doc.Pages.Insert(doc.Pages.IndexOf(page) + 1);
                        ip.Y = margin;
                    }
                }
            }
            // すべての目次ページを調べ、ドキュメントの先頭に挿入された目次ページを考慮して、
            // リンク先のページインデックスを更新します。
            int tocPages = doc.Pages.IndexOf(page) + 1;
            for (int i = 0; i < tocPages; ++i)
            {
                foreach (var ann in doc.Pages[i].Annotations)
                    if (ann is LinkAnnotation link && link.Dest is DestinationFit dest)
                        link.Dest = new DestinationFit(dest.PageIndex.Value + tocPages);
            }
            // PDF ドキュメントを保存します。
            doc.Save(stream);
            imageInfos.ForEach((ii_) => ii_.Image.Dispose());
            return doc.Pages.Count;
        }
    }
}