MergeRows.vb
'' 完毕:
Imports System.IO
Imports System.Drawing
Imports System.Text
Imports System.Data
Imports System.Linq
Imports System.Collections.Generic
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Html

'' 此示例展示了如何构建和呈现基于表格的报告
'' 按第一列分组,该列的单元格具有相同的
'' 价值观合并。
'' 该示例使用 HTML 中的 JavaScript 代码来实际
'' 合并单元格,演示 JavaScript 的使用
'' 将 HTML 渲染为 PDF。
'' 请注意,示例限制了行数,以便
'' 整个表格可以放在一页上。
'' 
'' 请参阅HelloWorldHtml顶部评论中的注释
'' 有关将 GcDocs.Html 添加到项目的详细信息的示例代码。
Public Class MergeRows
    Sub CreatePDF(ByVal stream As Stream)
        Const TTAG = "___TABLE___"

        '' HTML 页面模板:
        Const tableTpl =
            "<!DOCTYPE html>" +
            "<html>" +
            "<head>" +
            "<style>" +
            "" +
            "html * {" +
            "  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif !important;" +
            "}" +
            "" +
            "h1 {" +
            "  color: #1a5276;" +
            "  background-color: #d2b4de;" +
            "  text-align: center;" +
            "  padding: 6px;" +
            "}" +
            "" +
            "thead {display: table-header-group;}" +
            "" +
            "#products {" +
            "  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;" +
            "  border-collapse: collapse;" +
            "  width: 100%;" +
            "}" +
            "" +
            "#products td, #products th {" +
            "  border: 1px solid #ddd;" +
            "  padding: 8px;" +
            "}" +
            "" +
            "#products tr:hover {background-color: #ddd;}" +
            "" +
            "#products th {" +
            "  padding-top: 12px;" +
            "  padding-bottom: 12px;" +
            "  text-align: left;" +
            "  background-color: #a569bd;" +
            "  color: white;" +
            "}" +
            "</style>" +
            "" +
            "</head>" +
            "<body onload='mergeRows()'>" +
            "" +
            "<script>" +
            "function mergeRows() {" +
            "  const table = document.querySelector('table');" +
            "  let headerCell = null;" +
            "  for (let row of table.rows)" +
            "  {" +
            "    const firstCell = row.cells[0];" +
            "    if (headerCell === null || firstCell.innerText !== headerCell.innerText)" +
            "    {" +
            "      headerCell = firstCell;" +
            "    }" +
            "    else" +
            "    {" +
            "      headerCell.rowSpan++;" +
            "      firstCell.remove();" +
            "    }" +
            "  }" +
            "}" +
            "</script>" +
            "" +
            TTAG +
            "" +
            "</body>" +
            "</html>"

        Const tableHead = "<h1>Products by Suppliers</h1>"

        Const tableFmt =
            "<table id='products'>" +
            "  <thead>" +
            "    <th>Supplier</th>" +
            "    <th>Description</th>" +
            "    <th>Quantity Per Unit</th>" +
            "    <th>Unit Price</th>" +
            "  </thead>" +
            "{0}" +
            "</table>"

        Const dataRowFmt =
            "  <tr>" +
            "    <td>{0}</td>" +
            "    <td>{1}</td>" +
            "    <td>{2}</td>" +
            "    <td align='right'>{3:C}</td>" +
            "  </tr>"

        Using ds = New DataSet()
            ds.ReadXml(Path.Combine("Resources", "data", "DsNWind.xml"))

            Dim dtProds = ds.Tables("Products")
            Dim dtSupps = ds.Tables("Suppliers")

            Dim products =
            (From prod In dtProds.Select()
             Join supp In dtSupps.Select()
            On prod("SupplierID") Equals supp("SupplierID")
             Order By supp("CompanyName")
             Select New With {
                 .ProductName = prod("ProductName"),
                 .Supplier = supp("CompanyName"),
                 .QuantityPerUnit = prod("QuantityPerUnit"),
                 .UnitPrice = prod("UnitPrice")
            }).Take(16)

            Dim sb = New StringBuilder()
            sb.AppendLine(tableHead)
            For Each prod In products
                sb.AppendFormat(dataRowFmt, prod.Supplier, prod.ProductName, prod.QuantityPerUnit, prod.UnitPrice)
            Next

            Dim html = tableTpl.Replace(TTAG, String.Format(tableFmt, sb.ToString()))
            Dim tmp = Path.GetTempFileName()

            '' 创建一个用于呈现 HTML 的 GcHtmlBrowser 实例:
            Using browser = Util.NewHtmlBrowser()
                '' PdfOptions 允许您提供 HTML 到 PDF 转换的选项:
                Dim pdfOptions = New PdfOptions() With {
                    .Margins = New PdfMargins(0.2F, 1, 0.2F, 1),
                    .DisplayHeaderFooter = True,
                    .HeaderTemplate = "<div style='color:#1a5276; font-size:12px; width:1000px; margin-left:0.2in; margin-right:0.2in'>" +
                        "<span style='float:left;'>Product Price List</span>" +
                        "<span style='float:right'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></span>" +
                        "</div>",
                    .FooterTemplate = "<div style='color: #1a5276; font-size:12em; width:1000px; margin-left:0.2in; margin-right:0.2in;'>" +
                        "<span>(c) MESCIUS inc. All Rights Reserved.</span>" +
                        "<span style='float:right'>Generated on <span class='date'></span></span></div>"
                }
                '' 将源网页渲染到临时文件:
                Using htmlPage = browser.NewPage(html)
                    htmlPage.SaveAsPdf(tmp, pdfOptions)
                End Using
            End Using
            '' 将创建的 PDF 从临时文件复制到目标流:
            Using ts = File.OpenRead(tmp)
                ts.CopyTo(stream)
            End Using
            '' 清理:
            File.Delete(tmp)
        End Using
        '' 完毕:
    End Sub
End Class