MergeRows.cs
  1. //
  2. // This code is part of Document Solutions for Imaging demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Drawing;
  8. using System.Text;
  9. using System.Data;
  10. using System.Linq;
  11. using System.Collections.Generic;
  12. using GrapeCity.Documents.Drawing;
  13. using GrapeCity.Documents.Imaging;
  14. using GrapeCity.Documents.Text;
  15. using GrapeCity.Documents.Html;
  16.  
  17. namespace DsImagingWeb.Demos
  18. {
  19. // This sample shows how to build and render a table-based report
  20. // grouped by the first column, with that column's cells with same
  21. // values merged.
  22. // This sample uses a JavaScript code in the HTML to actually
  23. // merge the cells, demonstrating the use of JavaScript when
  24. // rendering HTML to images.
  25. // Note that the sample limits the number of rows so that
  26. // the whole table fits in the image.
  27. //
  28. // Please see notes in comments at the top of HelloWorldHtml
  29. // sample code for details on adding DsHtml to your projects.
  30. public class MergeRows
  31. {
  32. public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
  33. {
  34. const string TTAG = "___TABLE___";
  35.  
  36. // HTML page template:
  37. const string tableTpl =
  38. "<!DOCTYPE html>" +
  39. "<html>" +
  40. "<head>" +
  41. "<style>" +
  42.  
  43. "html * {" +
  44. " font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif !important;" +
  45. "}" +
  46.  
  47. "h1 {" +
  48. " color: #1a5276;" +
  49. " background-color: #d2b4de;" +
  50. " text-align: center;" +
  51. " padding: 6px;" +
  52. "}" +
  53.  
  54. "thead {display: table-header-group;}" +
  55.  
  56. "#products {" +
  57. " font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;" +
  58. " border-collapse: collapse;" +
  59. " width: 100%;" +
  60. "}" +
  61.  
  62. "#products td, #products th {" +
  63. " border: 1px solid #ddd;" +
  64. " padding: 8px;" +
  65. "}" +
  66.  
  67. // "#products tr:nth-child(even){background-color: #f2f2f2;}" +
  68.  
  69. "#products tr:hover {background-color: #ddd;}" +
  70.  
  71. "#products th {" +
  72. " padding-top: 12px;" +
  73. " padding-bottom: 12px;" +
  74. " text-align: left;" +
  75. " background-color: #a569bd;" +
  76. " color: white;" +
  77. "}" +
  78. "</style>" +
  79.  
  80. "</head>" +
  81. "<body onload='mergeRows()'>" +
  82.  
  83. // solution from https://stackoverflow.com/questions/56587070/merge-neighbouring-html-table-cells-with-same-value-using-js
  84. "<script>" +
  85. "function mergeRows() {" +
  86. " const table = document.querySelector('table');" +
  87. " let headerCell = null;" +
  88. " for (let row of table.rows)" +
  89. " {" +
  90. " const firstCell = row.cells[0];" +
  91. " if (headerCell === null || firstCell.innerText !== headerCell.innerText)" +
  92. " {" +
  93. " headerCell = firstCell;" +
  94. " }" +
  95. " else" +
  96. " {" +
  97. " headerCell.rowSpan++;" +
  98. " firstCell.remove();" +
  99. " }" +
  100. " }" +
  101. "}" +
  102. "</script>" +
  103.  
  104. TTAG +
  105.  
  106. "</body>" +
  107. "</html>";
  108.  
  109. const string tableHead = "<h1>Products by Suppliers</h1>";
  110.  
  111. const string tableFmt =
  112. "<table id='products'>" +
  113. " <thead>" +
  114. " <th>Supplier</th>" +
  115. " <th>Description</th>" +
  116. " <th>Quantity Per Unit</th>" +
  117. " <th>Unit Price</th>" +
  118. " </thead>" +
  119. "{0}" +
  120. "</table>";
  121.  
  122. const string dataRowFmt =
  123. " <tr>" +
  124. " <td>{0}</td>" +
  125. " <td>{1}</td>" +
  126. " <td>{2}</td>" +
  127. " <td align='right'>{3:C}</td>" +
  128. " </tr>";
  129.  
  130. DataSet ds = new DataSet();
  131. ds.ReadXml(Path.Combine("Resources", "data", "GcNWind.xml"));
  132.  
  133. DataTable dtProds = ds.Tables["Products"];
  134. DataTable dtSupps = ds.Tables["Suppliers"];
  135.  
  136. var products =
  137. (from prod in dtProds.Select()
  138. join supp in dtSupps.Select()
  139. on prod["SupplierID"] equals supp["SupplierID"]
  140. orderby supp["CompanyName"]
  141. select new
  142. {
  143. ProductName = prod["ProductName"],
  144. Supplier = supp["CompanyName"],
  145. QuantityPerUnit = prod["QuantityPerUnit"],
  146. UnitPrice = prod["UnitPrice"]
  147. }).Take(16);
  148.  
  149. var sb = new StringBuilder();
  150. sb.AppendLine(tableHead);
  151. foreach (var prod in products)
  152. sb.AppendFormat(dataRowFmt, prod.Supplier, prod.ProductName, prod.QuantityPerUnit, prod.UnitPrice);
  153.  
  154. var html = tableTpl.Replace(TTAG, string.Format(tableFmt, sb.ToString()));
  155.  
  156. var tfile = Path.GetTempFileName();
  157. var ms = new MemoryStream();
  158. // We use GcHtmlBrowser to render the whole generated HTML to an image.
  159. // Note that GcHtmlBrowser natively supports only JPEG, PNG and WEBP formats.
  160. // In this sample we limit the output to those formats.
  161. // For a more flexible approach that allows rendering HTML into any
  162. // image format supported by DsImaging, please see HtmlRenderPage0.
  163. using var browser = Common.Util.NewHtmlBrowser();
  164. using var htmlPage = browser.NewPage(html, new PageOptions() { WindowSize = pixelSize });
  165. tfile = Path.GetTempFileName();
  166. switch (targetMime)
  167. {
  168. case Common.Util.MimeTypes.JPEG:
  169. htmlPage.SaveAsJpeg(tfile);
  170. break;
  171. case Common.Util.MimeTypes.PNG:
  172. htmlPage.SaveAsPng(tfile);
  173. break;
  174. case Common.Util.MimeTypes.WEBP:
  175. htmlPage.SaveAsWebp(tfile);
  176. break;
  177. default:
  178. throw new Exception("Unsupported image format.");
  179. }
  180. // Copy the created image from the temp file to target stream:
  181. using (var ts = File.OpenRead(tfile))
  182. ts.CopyTo(ms);
  183. // Clean up:
  184. File.Delete(tfile);
  185. // Done.
  186. return ms;
  187. }
  188. }
  189. }
  190.