TextMap.cs
// 
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © MESCIUS inc. All rights reserved.
// 
using System;
using System.IO;
using System.Drawing;
using System.Numerics;
using System.Collections.Generic;
using System.Linq;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Pdf.Annotations;
using GrapeCity.Documents.Pdf.Graphics;
using GrapeCity.Documents.Pdf.TextMap;

namespace DsPdfWeb.Demos
{
    // テキストマップ機能を使用する方法です。
    // PDF ファイルのページ内のテキスト行の座標を検索して、
    // 特定の位置にあるテキストを取得する方法を示します。
    // このサンプルでは請求書形式の既存のPDFファイルを読み込んでいます。
    public class TextMap
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();

            var rc = Common.Util.AddNote(
                "このサンプルは、TimeSheet サンプルによって作成された PDF を一時的な GcPdfDocument に" +
                "読み込み、最初のページのテキストマップを取得して、マップ内のすべての線分の座標と" +
                "テキストを出力します。" +
                "また、マップの HitTest メソッドを使用して PDF 内の特定の座標にあるテキストを検索し、" +
                "結果を印刷します。" +
                "このサンプルで使用されている元の TimeSheet.pdf は参照用に追加されています。",
                page);

            // テキストの書式とレイアウトを設定します。
            var tf = new TextFormat()
            {
                Font = Common.Util.getFont(),
                FontSize = 12
            };
            var tfFound = new TextFormat()
            {
                Font = Common.Util.getFont(),
                FontSize = 13,
                ForeColor = Color.DarkBlue
            };
            var tl = new TextLayout(72)
            {
                MaxWidth = doc.PageSize.Width,
                MaxHeight = doc.PageSize.Height,
                MarginAll = rc.Left,
                MarginTop = rc.Bottom + 36,
                TabStops = new List<TabStop>() { new TabStop(72 * 2) },
            };
            var to = new TextSplitOptions(tl)
            {
                MinLinesInFirstParagraph = 2,
                MinLinesInLastParagraph = 2,
                RestMarginTop = rc.Left,
            };

            // 任意の PDF を開き、一時的なドキュメントに読み込んで、マップを使用してテキストを検索します。
            using var fs = File.OpenRead(Path.Combine("Resources", "PDFs", "simple_invoicejp.pdf"));
                var doc1 = new GcPdfDocument();
                doc1.Load(fs);
                var tmap = doc1.Pages[0].GetTextMap();

                // ページ上の特定の座標にあるテキストを取得します。
                float tx0 = 4.5f, ty0 = 2.5f, tx1 = 6.5f, ty1 = 2.7f;
                HitTestInfo htiFrom = tmap.HitTest(tx0 * 72, ty0 * 72);
                HitTestInfo htiTo = tmap.HitTest(tx1 * 72, ty1 * 72);
                tmap.GetFragment(htiFrom.Pos, htiTo.Pos, out TextMapFragment range1, out string text1);
                tl.AppendLine($"次の四角形領域の範囲にあるテキストを抽出 \n x={tx0:F2}\", y={ty0:F2}\", width={tx1 - tx0:F2}\", height={ty1 - ty0:F2}\" \n", tf);
                tl.AppendLine(text1, tfFound);
                tl.AppendLine();

                // ページ上のすべてのテキスト断片とその位置を取得します。
                tl.AppendLine("ページ上に表示されたすべてのテキストをリスト化", tf);
                tmap.GetFragment(out TextMapFragment range, out string text);
                foreach (TextLineFragment tlf in range)
                {
                    var coords = tmap.GetCoords(tlf);
                    tl.Append($"座標位置 ({coords.B.X / 72:F2}\",{coords.B.Y / 72:F2}\"):\t", tf);
                    tl.AppendLine(tmap.GetText(tlf), tfFound);
                }

                // 結果を印刷します。
                tl.PerformLayout(true);
                while (true)
                {
                    // 'rest' は、収まりきらなかったテキストを受け入れます。
                    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();
                }

                // 参照用に元の文書に追加します。
                doc.MergeWithDocument(doc1, new MergeDocumentOptions());
                // PDF ドキュメントを保存します。
                doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}