DynamicTable.cs
// // This code is part of Document Solutions for PDF demos. // Copyright (c) MESCIUS inc. All rights reserved. // using System; using System.IO; using System.Drawing; using System.Text; using GrapeCity.Documents.Pdf; using GrapeCity.Documents.Text; using GrapeCity.Documents.Html; namespace DsPdfWeb.Demos { // This sample shows how to insert an HTML table with a varying number of rows // that might not fit on a single page, into a PDF document starting at an arbitrary // position on the page (all data rows must have the same height though). // We first create a table with a single data row, measure its height, // then create a similar table but with two data rows and measure it too. // This allows us to find out the header and data rows' heights, and render // the table to a PDF starting at the desired position on the page, and split // it into additional pages as needed. // // Please see notes in comments at the top of HelloWorldHtml // sample code for details on adding DsHtml to your projects. // // See the LongTable demo for a different approach to creating a long // dynamic table that does not use DsHtml, and provides better performance. public class DynamicTable { public int CreatePDF(Stream stream) { // This tag is used to insert the prepared table HTML code // into the HTML page template that defines the CSS styles etc. // (Using this tag allows you to use string.Format when building // the table HTML code, as otherwise curly braces in style // definitions would interfere with format specifiers.) const string TTAG = "___TABLE___"; // The HTML page template: const string 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>"; // The table HTML code format: const string tableFmt = "<table id='employees'>" + " <tr>" + " <th>Index</th>" + " <th>Lorem</th>" + " <th>Ipsum</th>" + " </tr>" + "{0}" + "</table>"; // The table row HTML code format: const string dataRowFmt = " <tr>" + " <td>{0}</td>" + " <td>{1}</td>" + " <td>{2}</td>" + " </tr>"; // Create a new PDF document: var doc = new GcPdfDocument(); // Add a page: var page = doc.NewPage(); // Add a page, get its graphics: var g = page.Graphics; // Set up HTML to PDF formatting options. // The most important are the size limits, in this case // we do not limit the height as we will adjust it programmatically. // Note that in HtmlToPdfFormat, sizes are specified in inches: var hf = new HtmlToPdfFormat(false) { MaxPageWidth = page.Size.Width / 72 }; // HTML code for a single data row (with sample data): var dataRow = string.Format(dataRowFmt, "a", "b", "c"); // HTML page with a table that has a single data row: var thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow)); // Create an instance of GcHtmlBrowser that is used to render HTML: using var browser = Common.Util.NewHtmlBrowser(); // Measure the HTML for the current GcPdfGraphics: var s1 = g.MeasureHtml(browser, thtml, hf); // Same HTML page but with two data rows: thtml = tableTpl.Replace(TTAG, string.Format(tableFmt, dataRow + dataRow)); // Measure the new HTML: var s2 = g.MeasureHtml(browser, thtml, hf); // Calculate data row and header row heights: var rowHeight = s2.Height - s1.Height; var headerHeight = s1.Height - rowHeight; // Add a note at the top of the first page: var nrc = Common.Util.AddNote( "Here we render an HTML table with an unknown number of rows " + "that starts at a specified position on the first page, " + "and may span multiple pages.", page); // Set up for building the table with random data: var lorems = Common.Util.LoremWords(); var rnd = Common.Util.NewRandom(); var sb = new StringBuilder(); // Page layout parameters: var marginx = nrc.Left; var marginy = nrc.Top; var x = marginx; var y = nrc.Bottom + 36; var tbottom = nrc.Bottom + 36 + headerHeight; // A random number of data rows to render: int nrows = rnd.Next(100, 200); // Generate and render the table, adding continuation pages as needed: for (int i = 0; i < nrows; ++i) { sb.AppendFormat(dataRowFmt, i, lorems[rnd.Next(lorems.Count)], lorems[rnd.Next(lorems.Count)]); tbottom += rowHeight; var lastPage = i == nrows - 1; if (tbottom >= page.Size.Height - 72 || lastPage) { var html = tableTpl.Replace(TTAG, string.Format(tableFmt, sb.ToString())); var ok = g.DrawHtml(browser, html, x, y, new HtmlToPdfFormat(false) { MaxPageWidth = (page.Size.Width - marginx * 2) / 72 }, out SizeF size); if (!lastPage) { page = doc.NewPage(); g = page.Graphics; y = 72; tbottom = y + headerHeight; sb.Clear(); } } } // Done: doc.Save(stream); return doc.Pages.Count; } } }