DynamicTable.cs
// 完毕:
using System;
using System.IO;
using System.Drawing;
using System.Text;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Html;

namespace DsPdfWeb.Demos
{
    // 此示例演示如何插入具有不同行数的 HTML 表格
    // 可能无法容纳在单个页面上,而是以任意位置开始的 PDF 文档
    // 页面上的位置(但所有数据行必须具有相同的高度)。
    // 我们首先创建一个包含单个数据行的表,测量其高度,
    // 然后创建一个类似的表,但有两个数据行并对其进行测量。
    // 这使我们能够找出标题和数据行的高度,并渲染
    // 从页面上所需的位置开始将表格转换为 PDF,然后拆分
    // 根据需要将其放入其他页面。
    // 
    // 请参阅HelloWorldHtml顶部评论中的注释
    // 有关将 GcDocs.Html 添加到项目的详细信息的示例代码。
    public class DynamicTable
    {
        public int CreatePDF(Stream stream)
        {
            // 该标签用于插入准备好的表格HTML代码
            // 到定义 CSS 样式等的 HTML 页面模板中。
            // (使用此标签允许您在构建时使用 string.Format
            // 表 HTML 代码,否则样式中的大括号
            // 定义会干扰格式说明符。)
            const string TTAG = "___TABLE___";

            // HTML 页面模板:
            const string tableTpl =
                "<!DOCTYPE html>" +
                "<html>" +
                "<head>" +
                "<style>" +
                "#employees {" +
                "  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;" +
                "  border-collapse: collapse;" +
                "  width: 100%;" +
                "}" +

                "#employees td, #employees th {" +
                "  border: 1px solid #ddd;" +
                "  padding: 8px;" +
                "}" +

                "#employees tr:nth-child(even){background-color: #f2f2f2;}" +

                "#employees tr:hover {background-color: #ddd;}" +

                "#employees th {" +
                "  padding-top: 12px;" +
                "  padding-bottom: 12px;" +
                "  text-align: left;" +
                "  background-color: #3377ff;" +
                "  color: white;" +
                "}" +
                "</style>" +
                "</head>" +
                "<body>" +

                TTAG +

                "</body>" +
                "</html>";

            // 表格HTML代码格式:
            const string tableFmt =
                "<table id='employees'>" +
                "  <tr>" +
                "    <th>Index</th>" +
                "    <th>Lorem</th>" +
                "    <th>Ipsum</th>" +
                "  </tr>" +
                "{0}" +
                "</table>";

            // 表格行HTML代码格式:
            const string dataRowFmt =
                "  <tr>" +
                "    <td>{0}</td>" +
                "    <td>{1}</td>" +
                "    <td>{2}</td>" +
                "  </tr>";

            // 创建一个新的 PDF 文档:
            var doc = new GcPdfDocument();
            // 添加页面:
            var page = doc.NewPage();
            // 添加一个页面,获取其图形:
            var g = page.Graphics;

            // 设置 HTML 到 PDF 格式选项。
            // 最重要的是大小限制,在这种情况下
            // 我们不限制高度,因为我们将以编程方式调整它。
            // 请注意,在 HtmlToPdfFormat 中,尺寸以英寸为单位指定:
            var hf = new HtmlToPdfFormat(false) { MaxPageWidth = page.Size.Width / 72 };

            // 单个数据行的 HTML 代码(带有示例数据):
            var dataRow = string.Format(dataRowFmt, "a", "b", "c");
            // 包含一个包含单个数据行的表的 HTML 页面:
            var thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow));

            // 创建一个用于呈现 HTML 的 GcHtmlBrowser 实例:
            using var browser = Common.Util.NewHtmlBrowser();

            // 测量当前 GcPdfGraphics 的 HTML:
            var s1 = g.MeasureHtml(browser, thtml, hf);
            // 相同的 HTML 页面,但有两个数据行:
            thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow + dataRow));
            // 测量新的 HTML:
            var s2 = g.MeasureHtml(browser, thtml, hf);
            // 计算数据行和标题行高度:
            var rowHeight = s2.Height - s1.Height;
            var headerHeight = s1.Height - rowHeight;

            // 在第一页顶部添加注释:
            var nrc = Common.Util.AddNote(
                "这里我们渲染一个行数未知的 HTML 表格" +
                "",
                page);

            // 设置使用随机数据构建表:
            var lorems = Common.Util.LoremWords();
            var rnd = Common.Util.NewRandom();
            var sb = new StringBuilder();

            // 页面布局参数:
            var marginx = nrc.Left;
            var marginy = nrc.Top;
            var x = marginx;
            var y = nrc.Bottom + 36;
            var tbottom = nrc.Bottom + 36 + headerHeight;
            // 要呈现的随机数量的数据行:
            int nrows = rnd.Next(100, 200);
            // 生成并呈现表格,根据需要添加延续页面:
            for (int i = 0; i < nrows; ++i)
            {
                sb.AppendFormat(dataRowFmt, i, lorems[rnd.Next(lorems.Count)], lorems[rnd.Next(lorems.Count)]);
                tbottom += rowHeight;
                var lastPage = i == nrows - 1;
                if (tbottom >= page.Size.Height - 72 || lastPage)
                {
                    var html = tableTpl.Replace(TTAG, string.Format(tableFmt, sb.ToString()));
                    var ok = g.DrawHtml(browser, html, x, y,
                        new HtmlToPdfFormat(false) { MaxPageWidth = (page.Size.Width - marginx * 2) / 72 },
                        out SizeF size);
                    if (!lastPage)
                    {
                        page = doc.NewPage();
                        g = page.Graphics;
                        y = 72;
                        tbottom = y + headerHeight;
                        sb.Clear();
                    }
                }
            }

            // 完毕:
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}