MergeRows.cs
// 
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// Copyright (c) GrapeCity inc. All rights reserved.
// 
using System;
using System.IO;
using System.Drawing;
using System.Text;
using System.Data;
using System.Linq;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Html;

namespace GcPdfWeb.Samples
{
    // このサンプルでは、最初の列でグループ化されたテーブルベースのレポートを作成
    // およびレンダリングし、同じ列のセルを同じ値で結合する方法を示します。
    // このサンプルでは、HTML の JavaScript コードを使用して実際にセルをマージし、
    // HTML を PDF にレンダリングする際の JavaScript の使用方法を示します。
    // サンプルでは、テーブル全体が1ページに収まるように行数が制限されています。
    // 
    // GcHtml をプロジェクトに追加する方法の詳細については、HelloWorldHtml
    // サンプルコードの上部にあるコメントのメモを参照してください。
    public class MergeRows
    {
        public void CreatePDF(Stream stream)
        {
            const string TTAG = "___TABLE___";

            // HTML ページテンプレート:
            const string 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()'>" +

                // solution from https://stackoverflow.com/questions/56587070/merge-neighbouring-html-table-cells-with-same-value-using-js
                "<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 string tableHead = "<h1>仕入れ先別商品</h1>";

            const string tableFmt =
                "<table id='products'>" +
                "  <thead>" +
                "    <th>仕入れ先</th>" +
                "    <th>商品名</th>" +
                "    <th>数量</th>" +
                "    <th>単価</th>" +
                "  </thead>" +
                "{0}" +
                "</table>";

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

            using (var ds = new DataSet())
            {
                ds.ReadXml(Path.Combine("Resources", "data", "GcNWind.xml"));

                DataTable dtProds = ds.Tables["Products"];
                DataTable dtSupps = ds.Tables["Suppliers"];

                var products =
                    (from prod in dtProds.Select()
                     join supp in dtSupps.Select()
                 on prod["SupplierID"] equals supp["SupplierID"]
                     orderby supp["CompanyName"]
                     select new
                     {
                         ProductName = prod["ProductName"],
                         Supplier = supp["CompanyName"],
                         QuantityPerUnit = prod["QuantityPerUnit"],
                         UnitPrice = prod["UnitPrice"]
                     }).Take(16);

                var sb = new StringBuilder();
                sb.AppendLine(tableHead);
                foreach (var prod in products)
                    sb.AppendFormat(dataRowFmt, prod.Supplier, prod.ProductName, prod.QuantityPerUnit, prod.UnitPrice);

                var html = tableTpl.Replace(TTAG, string.Format(tableFmt, sb.ToString()));

                var tmp = Path.GetTempFileName();
                using (var re = new GcHtmlRenderer(html))
                {
                    // PdfSettings では、HTML から PDF への変換のオプションを提供できます。
                    var pdfSettings = new PdfSettings()
                    {
                        Margins = new Margins(0.2f, 1, 0.2f, 1),
                        IgnoreCSSPageSize = true,
                        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) GrapeCity inc. All Rights Reserved.</span>" +
                            "<span style='float:right'>Generated on <span class='date'></span></span></div>"
                    };
                    // ソース Web ページを一時ファイルにレンダリングします。
                    re.RenderToPdf(tmp, pdfSettings);
                }
                // 作成した PDF を一時ファイルからターゲットストリームにコピーします。
                using (var ts = File.OpenRead(tmp))
                    ts.CopyTo(stream);
                // 一時ファイルを削除します。
                File.Delete(tmp);
            }
            // PDF ドキュメントを保存します。
        }
    }
}