MergeRows.vb
''
'' This code is part of GrapeCity Documents for Imaging samples.
'' Copyright (c) GrapeCity, 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 GcHtml to your projects.
Public Class MergeRows
    Function GenerateImageStream(
                ByVal targetEncoding As ImageEncoding,
                Optional pixelWidth As Integer = 1024,
                Optional pixelHeight As Integer = 1024,
                Optional opaque As Boolean = True,
                Optional dpiX As Single = 96,
                Optional dpiY As Single = 96) 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 GcHtmlRenderer to render the whole generated HTML to an image.
        '' Note that GcHtmlRenderer natively supports only JPEG and PNG.
        '' In this sample we limite the output to those two formats.
        '' For a more flexible approach that allows to render HTML into any
        '' image format supported by GcImaging, please see HtmlRenderPage0.
        Using re = New GcHtmlRenderer(html)
            Select Case targetEncoding
                Case ImageEncoding.Jpeg
                    tfile = Path.GetTempFileName()
                    re.RenderToJpeg(tfile, New JpegSettings() With {.WindowSize = New Size(pixelWidth, pixelHeight)})

                Case ImageEncoding.Png
                    tfile = Path.GetTempFileName()
                    re.RenderToPng(tfile, New PngSettings() With {.WindowSize = New Size(pixelWidth, pixelHeight)})
                Case Else
                    Throw New Exception("Only JPEG and PNG are supported.")
            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