TextMap.cs
// 完毕:
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 是由 TimeSheet 创建的。
    public class TextMap
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();

            var rc = Common.Util.AddNote(
                "此示例将 TimeSheet 示例创建的 PDF 加载到临时 GcPdfDocument 中," +
                "",
                page);

            // 设置文本格式和布局:
            var tf = new TextFormat()
            {
                Font = StandardFonts.Times,
                FontSize = 13
            };
            var tfFound = new TextFormat()
            {
                Font = StandardFonts.TimesBold,
                FontSize = 14,
                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", "TimeSheet.pdf"));
            var doc1 = new GcPdfDocument();
            doc1.Load(fs);
            var tmap = doc1.Pages[0].GetTextMap();

            // 我们在页面上特定的(我们已知的)几何位置检索文本:
            float tx0 = 2.1f, ty0 = 3.37f, tx1 = 3.1f, ty1 = 3.4f;
            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($"Looked for text in rectangle x={tx0:F2}\", y={ty0:F2}\", width={tx1 - tx0:F2}\", height={ty1 - ty0:F2}\", found:", tf);
            tl.AppendLine(text1, tfFound);
            tl.AppendLine();

            // 获取所有文本片段及其在页面上的位置:
            tl.AppendLine("List of all texts found on the page", tf);
            tmap.GetFragment(out TextMapFragment range, out string text);
            foreach (TextLineFragment tlf in range)
            {
                var coords = tmap.GetCoords(tlf);
                tl.Append($"Text at ({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());
            // 完毕:
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}