DynamicTable.cs
  1. //
  2. // This code is part of Document Solutions for PDF 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 GrapeCity.Documents.Pdf;
  10. using GrapeCity.Documents.Text;
  11. using GrapeCity.Documents.Html;
  12.  
  13. namespace DsPdfWeb.Demos
  14. {
  15. // This sample shows how to insert an HTML table with a varying number of rows
  16. // that might not fit on a single page, into a PDF document starting at an arbitrary
  17. // position on the page (all data rows must have the same height though).
  18. // We first create a table with a single data row, measure its height,
  19. // then create a similar table but with two data rows and measure it too.
  20. // This allows us to find out the header and data rows' heights, and render
  21. // the table to a PDF starting at the desired position on the page, and split
  22. // it into additional pages as needed.
  23. //
  24. // Please see notes in comments at the top of HelloWorldHtml
  25. // sample code for details on adding DsHtml to your projects.
  26. //
  27. // See the LongTable demo for a different approach to creating a long
  28. // dynamic table that does not use DsHtml, and provides better performance.
  29. public class DynamicTable
  30. {
  31. public int CreatePDF(Stream stream)
  32. {
  33. // This tag is used to insert the prepared table HTML code
  34. // into the HTML page template that defines the CSS styles etc.
  35. // (Using this tag allows you to use string.Format when building
  36. // the table HTML code, as otherwise curly braces in style
  37. // definitions would interfere with format specifiers.)
  38. const string TTAG = "___TABLE___";
  39.  
  40. // The HTML page template:
  41. const string tableTpl =
  42. "<!DOCTYPE html>" +
  43. "<html>" +
  44. "<head>" +
  45. "<style>" +
  46. "#employees {" +
  47. " font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;" +
  48. " border-collapse: collapse;" +
  49. " width: 100%;" +
  50. "}" +
  51.  
  52. "#employees td, #employees th {" +
  53. " border: 1px solid #ddd;" +
  54. " padding: 8px;" +
  55. "}" +
  56.  
  57. "#employees tr:nth-child(even){background-color: #f2f2f2;}" +
  58.  
  59. "#employees tr:hover {background-color: #ddd;}" +
  60.  
  61. "#employees th {" +
  62. " padding-top: 12px;" +
  63. " padding-bottom: 12px;" +
  64. " text-align: left;" +
  65. " background-color: #3377ff;" +
  66. " color: white;" +
  67. "}" +
  68. "</style>" +
  69. "</head>" +
  70. "<body>" +
  71.  
  72. TTAG +
  73.  
  74. "</body>" +
  75. "</html>";
  76.  
  77. // The table HTML code format:
  78. const string tableFmt =
  79. "<table id='employees'>" +
  80. " <tr>" +
  81. " <th>Index</th>" +
  82. " <th>Lorem</th>" +
  83. " <th>Ipsum</th>" +
  84. " </tr>" +
  85. "{0}" +
  86. "</table>";
  87.  
  88. // The table row HTML code format:
  89. const string dataRowFmt =
  90. " <tr>" +
  91. " <td>{0}</td>" +
  92. " <td>{1}</td>" +
  93. " <td>{2}</td>" +
  94. " </tr>";
  95.  
  96. // Create a new PDF document:
  97. var doc = new GcPdfDocument();
  98. // Add a page:
  99. var page = doc.NewPage();
  100. // Add a page, get its graphics:
  101. var g = page.Graphics;
  102.  
  103. // Set up HTML to PDF formatting options.
  104. // The most important are the size limits, in this case
  105. // we do not limit the height as we will adjust it programmatically.
  106. // Note that in HtmlToPdfFormat, sizes are specified in inches:
  107. var hf = new HtmlToPdfFormat(false) { MaxPageWidth = page.Size.Width / 72 };
  108.  
  109. // HTML code for a single data row (with sample data):
  110. var dataRow = string.Format(dataRowFmt, "a", "b", "c");
  111. // HTML page with a table that has a single data row:
  112. var thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow));
  113.  
  114. // Create an instance of GcHtmlBrowser that is used to render HTML:
  115. using var browser = Common.Util.NewHtmlBrowser();
  116.  
  117. // Measure the HTML for the current GcPdfGraphics:
  118. var s1 = g.MeasureHtml(browser, thtml, hf);
  119. // Same HTML page but with two data rows:
  120. thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow + dataRow));
  121. // Measure the new HTML:
  122. var s2 = g.MeasureHtml(browser, thtml, hf);
  123. // Calculate data row and header row heights:
  124. var rowHeight = s2.Height - s1.Height;
  125. var headerHeight = s1.Height - rowHeight;
  126.  
  127. // Add a note at the top of the first page:
  128. var nrc = Common.Util.AddNote(
  129. "Here we render an HTML table with an unknown number of rows " +
  130. "that starts at a specified position on the first page, " +
  131. "and may span multiple pages.",
  132. page);
  133.  
  134. // Set up for building the table with random data:
  135. var lorems = Common.Util.LoremWords();
  136. var rnd = Common.Util.NewRandom();
  137. var sb = new StringBuilder();
  138.  
  139. // Page layout parameters:
  140. var marginx = nrc.Left;
  141. var marginy = nrc.Top;
  142. var x = marginx;
  143. var y = nrc.Bottom + 36;
  144. var tbottom = nrc.Bottom + 36 + headerHeight;
  145. // A random number of data rows to render:
  146. int nrows = rnd.Next(100, 200);
  147. // Generate and render the table, adding continuation pages as needed:
  148. for (int i = 0; i < nrows; ++i)
  149. {
  150. sb.AppendFormat(dataRowFmt, i, lorems[rnd.Next(lorems.Count)], lorems[rnd.Next(lorems.Count)]);
  151. tbottom += rowHeight;
  152. var lastPage = i == nrows - 1;
  153. if (tbottom >= page.Size.Height - 72 || lastPage)
  154. {
  155. var html = tableTpl.Replace(TTAG, string.Format(tableFmt, sb.ToString()));
  156. var ok = g.DrawHtml(browser, html, x, y,
  157. new HtmlToPdfFormat(false) { MaxPageWidth = (page.Size.Width - marginx * 2) / 72 },
  158. out SizeF size);
  159. if (!lastPage)
  160. {
  161. page = doc.NewPage();
  162. g = page.Graphics;
  163. y = 72;
  164. tbottom = y + headerHeight;
  165. sb.Clear();
  166. }
  167. }
  168. }
  169.  
  170. // Done:
  171. doc.Save(stream);
  172. return doc.Pages.Count;
  173. }
  174. }
  175. }
  176.