GetTablePages.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.Recognition;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Common;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos
{
    // テーブルからデータを抽出します。
    public class GetTablePages
    {
        public int CreatePDF(Stream stream)
        {
            const float DPI = 72;
            const float margin = 36;
            var doc = new GcPdfDocument();

            var tf = new TextFormat()
            {
                Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "ipag.ttc")),
                FontSize = 9,
                ForeColor = Color.Black
            };
            var tfHdr = new TextFormat(tf)
            {
                FontBold = true,
                FontSize = 11,
                ForeColor = Color.DarkBlue
            };
            var tfRed = new TextFormat(tf) { ForeColor = Color.Red };

            using (var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "product-list.pdf")))
            {
                var page = doc.NewPage();
                page.Landscape = true;
                var g = page.Graphics;

                var rc = Common.Util.AddNote(
                    "このサンプルでは、複数のページに分割されたテーブル(商品価格表)を含むPDFを読み込み、" +
                    "Page.GetTable()メソッドを用いて全ページのテーブルを抽出しています。" +
                    "抽出されたデータは、行とセルのリストとして出力されます。" +
                    "テーブルを含む元のPDFも、参照用に生成されるPDFに追加されます。",
                    page,
                    new RectangleF(margin, margin, page.Bounds.Width - margin * 2, page.Bounds.Height - margin * 2));

                var tl = g.CreateTextLayout();
                tl.MaxWidth = page.Bounds.Width;
                tl.MaxHeight = page.Bounds.Height;
                tl.MarginAll = margin;
                tl.MarginTop = rc.Bottom;
                tl.DefaultTabStops = 165;

                var docSrc = new GcPdfDocument();
                docSrc.Load(fs);

                for (int i = 0; i < docSrc.Pages.Count; ++i)
                {
                    // TableExtractOptionsを使用すると、テーブルのフォーマットの特殊性を考慮して、
                    // テーブルの認識を微調整することができます。
                    var teo = new TableExtractOptions();
                    var GetMinimumDistanceBetweenRows = teo.GetMinimumDistanceBetweenRows;
                    // ここでは、行間の最小距離をわずかに長くして、テキストが折り返されているセルが
                    // 2つのセルであると誤認識されないようにしています。
                    teo.GetMinimumDistanceBetweenRows = (list) => {
                        var res = GetMinimumDistanceBetweenRows(list);
                        return res * 1.2f;
                    };
                    var top = i == 0 ? DPI * 2 : DPI;
                    // 指定された範囲にあるテーブルを取得します。
                    var itable = docSrc.Pages[i].GetTable(new RectangleF(DPI * 0.25f, top, DPI * 8, DPI * 10.5f - top), teo);

                    // テキストレイアウトにテーブルデータを追加します。
                    tl.Append($"\n元のドキュメントの {i + 1} ページ目のテーブルには、列が {itable.Cols.Count} 列と、行が {itable.Rows.Count} 行あります。データは以下のとおりです。", tfHdr);
                    tl.AppendParagraphBreak();
                    for (int row = 0; row < itable.Rows.Count; ++row)
                    {
                        var tfmt = row == 0 ? tfHdr : tf;
                        for (int col = 0; col < itable.Cols.Count; ++col)
                        {
                            var cell = itable.GetCell(row, col);
                            if (col > 0)
                                tl.Append("\t", tfmt);
                            if (cell == null)
                                tl.Append("<セルなし>", tfRed);
                            else
                                tl.Append(cell.Text, tfmt);
                        }
                        tl.AppendLine();
                    }
                }

                // 抽出したデータを出力します。
                TextSplitOptions to = new TextSplitOptions(tl) { RestMarginTop = margin, MinLinesInFirstParagraph = 2, MinLinesInLastParagraph = 2 };
                tl.PerformLayout(true);
                while (true)
                {
                    var splitResult = tl.Split(to, out TextLayout rest);
                    doc.Pages.Last.Graphics.DrawTextLayout(tl, PointF.Empty);
                    if (splitResult != SplitResult.Split)
                        break;
                    tl = rest;
                    doc.NewPage().Landscape = true;
                }

                // 参照のため元のドキュメントを追加します。
                doc.MergeWithDocument(docSrc);

                doc.Save(stream);
                return doc.Pages.Count;
            }
        }
    }
}