MergeRows.vb
''
'' This code is part of Document Solutions for Imaging demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Text
Imports System.Data
Imports System.Linq
Imports System.Collections.Generic
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Imaging
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Html

'' This sample shows how to build and render a table-based report
'' grouped by the first column, with that column's cells with same
'' values merged.
'' This sample uses a JavaScript code in the HTML to actually
'' merge the cells, demonstrating the use of JavaScript when
'' rendering HTML to images.
'' Note that the sample limits the number of rows so that
'' the whole table fits in the image.
''
'' Please see notes in comments at the top of HelloWorldHtml
'' sample code for details on adding DsHtml to your projects.
Public Class MergeRows
    Function GenerateImageStream(
                ByVal targetMime As String,
                ByVal pixelSize As Size,
                ByVal dpi As Single,
                ByVal opaque As Boolean,
                Optional sampleParams As String() = Nothing) As Stream

        Const TTAG = "___TABLE___"

        '' HTML page template
        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>"

        Dim ds = New DataSet()
        ds.ReadXml(Path.Combine("Resources", "data", "GcNWind.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 tfile = Path.GetTempFileName()
        Dim ms = New MemoryStream()
        '' We use GcHtmlBrowser to render the whole generated HTML to an image.
        '' Note that GcHtmlBrowser natively supports only JPEG, PNG and WEBP formats.
        '' In this sample we limit the output to those formats.
        '' For a more flexible approach that allows rendering HTML into any
        '' image format supported by DsImaging, please see HtmlRenderPage0.
        Using browser = Util.NewHtmlBrowser(), htmlPage = browser.NewPage(html, New PageOptions() With {.WindowSize = pixelSize})
            Select Case targetMime
                Case MimeTypes.JPEG
                    htmlPage.SaveAsJpeg(tfile)
                Case MimeTypes.PNG
                    htmlPage.SaveAsPng(tfile)
                Case MimeTypes.WEBP
                    htmlPage.SaveAsWebp(tfile)
                Case Else
                    Throw New Exception("Unsupported image format.")
            End Select
        End Using
        '' Copy the created image from the temp file to target stream:
        Using ts = File.OpenRead(tfile)
            ts.CopyTo(ms)
        End Using
        '' Clean up:
        File.Delete(tfile)
        '' Done.
        Return ms
    End Function
End Class