DynamicTable.vb
'' 
'' このコードは、DioDocs for PDF のサンプルの一部として提供されています。
'' © MESCIUS inc. All rights reserved.
'' 
Imports System.IO
Imports System.Drawing
Imports System.Text
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Html

'' このサンプルでは、1ページに収まらない可能性があるさまざまな行数の
'' HTML テーブルを、ページ上の任意の位置から始まるPDFドキュメントに
'' 挿入する方法を示します(ただし、すべてのデータ行は同じ高さでなけ
'' ればなりません)。
'' 最初に単一のデータ行を持つテーブルを作成し、その高さを測定します。
'' 次に、2つのデータ行を持つ同様のテーブルを作成し、それも測定します。
'' これにより、ヘッダーとデータ行の高さを確認し、テーブルをページ上の
'' 目的の位置から PDF にレンダリングし、必要に応じて追加のページに
'' 分割できます。
'' 
'' DsHtml をプロジェクトに追加する方法の詳細については、
'' HelloWorldHtml サンプルコードの上部にあるコメントのメモを
'' 参照してください。
Public Class DynamicTable
    Function CreatePDF(ByVal stream As Stream) As Integer
        '' このタグは、CSS スタイルなどを定義するHTMLページテンプレートに
        '' 準備済みテーブル HTML コードを挿入するために使用されます。
        '' (このタグを使用すると、表の HTML コードを作成するときに
        '' string.Format を使用できます。そうしないと、スタイル定義内の
        '' 中括弧が書式指定子に干渉するためです。)
        Const TTAG = "___TABLE___"

        '' HTML ページテンプレート。
        Const 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 tableFmt =
                "<table id='employees'>" +
                "  <tr>" +
                "    <th>Index</th>" +
                "    <th>Lorem</th>" +
                "    <th>Ipsum</th>" +
                "  </tr>" +
                "{0}" +
                "</table>"

        '' テーブル行の HTML コード書式。
        Const dataRowFmt =
                "  <tr>" +
                "    <td>{0}</td>" +
                "    <td>{1}</td>" +
                "    <td>{2}</td>" +
                "  </tr>"

        '' 新規 PDF ドキュメントを作成します。
        Dim doc = New GcPdfDocument()
        '' ページを追加します。
        Dim page = doc.NewPage()
        '' ページを追加し、そのグラフィックを取得します。
        Dim g = page.Graphics

        '' HTML から PDF へのフォーマットオプションを設定します。
        '' 最も重要なのはサイズの制限です。この場合、プログラムで調整
        '' するため、高さは制限しません。
        '' HtmlToPdfFormat では、サイズはインチで指定されることに注意してください。
        Dim hf = New HtmlToPdfFormat(False) With {.MaxPageWidth = page.Size.Width / 72}

        '' 単一のデータ行の HTML コード(サンプルデータ付き)。
        Dim dataRow = String.Format(dataRowFmt, "a", "b", "c")
        '' 単一のデータ行を持つテーブルを含む HTML ページ。
        Dim thtml = tableTpl.Replace(TTAG, String.Format(tableFmt, dataRow))

        '' HTML のレンダリングに使用する GcHtmlBrowser のインスタンスを生成します。
        Using browser = Util.NewHtmlBrowser()

            '' 現在の GcPdfGraphics の HTML を測定します。
            Dim s1 = g.MeasureHtml(browser, thtml, hf)
            '' 同じ HTML ページですが、2つのデータ行があります。
            thtml = tableTpl.Replace(TTAG, String.Format(tableFmt, dataRow + dataRow))
            '' 新しい HTML を測定します。
            Dim s2 = g.MeasureHtml(browser, thtml, hf)
            '' データ行とヘッダー行の高さを計算します。
            Dim rowHeight = s2.Height - s1.Height
            Dim headerHeight = s1.Height - rowHeight

            '' 最初のページの上部にメモを追加します。
            Dim nrc = Util.AddNote(
                "ここでは、最初のページの指定された位置から始まり、複数のページにまたがる" +
                "可能性のある未知の行数で HTML テーブルをレンダリングします。",
                page)

            '' ランダムデータでテーブルを構築するための設定。
            Dim lorems = Util.LoremWords()
            Dim rnd = Util.NewRandom()
            Dim sb = New StringBuilder()

            '' ページレイアウトパラメータ。
            Dim marginx = nrc.Left
            Dim marginy = nrc.Top
            Dim x = marginx
            Dim y = nrc.Bottom + 36
            Dim tbottom = nrc.Bottom + 36 + headerHeight
            '' レンダリングするランダムな数のデータ行。
            Dim nrows = rnd.Next(100, 200)
            '' テーブルを生成してレンダリングし、必要に応じて継続ページを追加します。
            For i = 0 To nrows - 1
                sb.AppendFormat(dataRowFmt, i, lorems(rnd.Next(lorems.Count)), lorems(rnd.Next(lorems.Count)))
                tbottom += rowHeight
                Dim lastPage = i = (nrows - 1)
                If tbottom >= page.Size.Height - 72 Or lastPage Then
                    Dim html = tableTpl.Replace(TTAG, String.Format(tableFmt, sb.ToString()))
                    Dim size As SizeF
                    Dim ok = g.DrawHtml(browser, html, x, y, New HtmlToPdfFormat(False) With {.MaxPageWidth = (page.Size.Width - marginx * 2) / 72}, size)
                    If Not lastPage Then
                        page = doc.NewPage()
                        g = page.Graphics
                        y = 72
                        tbottom = y + headerHeight
                        sb.Clear()
                    End If
                End If
            Next

            '' PDF ドキュメントを保存します。
            doc.Save(stream)
            Return doc.Pages.Count
        End Using
    End Function
End Class