Generating HTML

Generating HTML

The Hypertext Markup Language, better known by its acronym HTML, is the most widespread format for content on the Web. HTML is the format web browsers typically read; it is a standard defined by the W3C (World Wide Web Consortium,, which is one of the bodies controlling the Internet. The HTML standard document is available on along with and some interesting links.

Delphi's HTML Producer Components

Delphi's HTML producer components (on the Internet page of the Component Palette) can be used to generate the HTML files and particularly to turn a database table into an HTML table. Many developers believe that the use of these components makes sense only when writing a web server extension. Although they were introduced for this purpose and are part of the WebBroker technology, you can still use three out of the five producer components in any application in which you must generate a static HTML file.

Before looking at the HtmlProd example, which demonstrates the use of these HTML producer components, let me summarize their role:

  • The simplest HTML producer component is the PageProducer, which manipulates an HTML file in which you've embedded special tags. The HTML can be stored in an external file or an internal string list. The advantage of this approach is that you can generate such a file using the HTML editor you prefer. At run time, the PageProducer converts the special tags to HTML code, giving you a straightforward method for modifying sections of an HTML document. The special tags have the basic format <#tagname>, but you can also supply named parameters within the tag. You'll process the tags in the OnTag event handler of the PageProducer.

  • The DataSetPageProducer extends the PageProducer by automatically replacing tags corresponding to field names of a connected data source.

  • The DataSetTableProducer component is generally useful for displaying the contents of a table, query, or other dataset. The idea is to produce an HTML table from a dataset, in a simple yet flexible way. The component has a nice preview, so you can see how the HTML output will look in a browser directly at design time.

  • The QueryTableProducer and the SQLQueryTableProducer components are similar to the DataSetTableProducer, but they are specifically tailored for building parametric queries (for the BDE or dbExpress, respectively) based on input from an HTML search form. This component makes little sense in a stand-alone program, and for this reason, I'll delay covering these components until Chapter 20.

Producing HTML Pages

A very simple example of using tags (introduced by the # symbol) is creating an HTML file that displays fields with the current date or a date computed relative to the current date, such as an expiration date. If you examine the HtmlProd example, you'll find a PageProducer1 component with internal HTML code, specified by the HTMLDoc string list:

<title>Producer Demo</title>
<h1>Producer Demo</h1>
<p>This is a demo of the page produced by the <b><#appname></b> application on
<p>The prices in this catalog are valid until <b><#expiration

If you prepare this file with an HTML editor (something I suggest you do), it may automatically place quotes around tag parameters, as in days="21", because this format is required by HTML 4 and XHTML. The PageProducer component has a StripParamQuotes property you can activate to remove those extra quotes when the component parses the code (before calling the OnHTMLTag event handler).

The Demo Page button copies the PageProducer component's output to the Text of a Memo. As you call the Content function of the PageProducer component, it reads the input HTML code, parses it, and triggers the OnTag event handler for every special tag. In the handler for this event, the program checks the value of the tag (passed in the TagString parameter) and returns a different HTML text (in the ReplaceText reference parameter), producing the output shown in Figure 19.7.

Click To expand Figure 19.7: The output of the HtmlProd example, a simple demonstra-tion of the Page-Producer component, when the user clicks the Demo Page button
procedure TFormProd.PageProducer1HTMLTag(Sender: TObject;
  Tag: TTag; const TagString: String; TagParams: TStrings;
  var ReplaceText: String);
  nDays: Integer;
  if TagString = 'date' then
    ReplaceText := DateToStr (Now)
  else if TagString = 'appname' then
    ReplaceText := ExtractFilename (Forms.Application.Exename)
  else if TagString = 'expiration' then
    nDays := StrToIntDef (TagParams.Values['days'], 0);
    if nDays <> 0 then
      ReplaceText := DateToStr (Now + nDays)
      ReplaceText := '<i>{expiration tag error}</i>';

Notice in particular the code I've written to convert the last tag, #expiration, which requires a parameter. The PageProducer places the entire text of the tag parameter (in this case, days=21) in a string that's part of the TagParams list. To extract the value portion of this string (the portion after the equal sign), you can use the Values property of the TagParams string list and search for the proper entry at the same time. If it can't locate the parameter or if the parameter's value isn't an integer, the program displays an error message.


The PageProducer component supports user-defined tags, which can be any string you like, but you should first review the special tags defined by the TTags enumeration. The possible values include tgLink (for the link tag), tgImage (for the img tag), tgTable (for the table tag), and a few others. If you create a custom tag, as in the PageProd example, the value of the Tag parameter to the HTMLTag handler will be tgCustom.

Producing Pages of Data

The HtmlProd example also has a DataSetPageProducer component, which is connected with a database table and with the following HTML source code:

<html><head><title>Data for <#name></title></head>
<h1><center>Data for <#name></center></h1>
<p>Capital: <#capital></p>
<p>Continent: <#continent></p>
<p>Area: <#area></p>
<p>Population: <#population></p><hr>
<p>Last updated on <#date><br>
HTML file produced by the program <#program>.</p>

By using tags with the names of the connected dataset's fields (the usual COUNTRY.DB database table), the program automatically gets the value of the current record's fields and replaces them automatically. This produces the output shown in Figure 19.8; the browser is connected to the HtmlProd example working as an HTTP server, as I'll discuss later. In the source code of the program related to this component, there is no reference to the database data:

procedure TFormProd.DataSetPageProducer1HTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
  if TagString = 'program' then
    ReplaceText := ExtractFilename (Forms.Application.Exename)
  else if TagString = 'date' then
    ReplaceText := DateToStr (Date);
Click To expand
Figure 19.8:  The output of the HtmlProd example for the Print Line button

Producing HTML Tables

The third button in the HtmlProd example is Print Table. This button is connected to a DataSetTableProducer component, again calling its Content function and copying its result to the Text of the Memo. By connecting the DataSet property of the DataSetTableProducer to ClientDataSet1, you can produce a standard HTML table.

The component by default generates only 20 rows, as indicated by the MaxRows property. If you want to get all of the table's records, you can set this property to -1—a simple but undocumented setting.


The DataSetTableProducer component starts from the current record rather than from the first one. So, the second time you click the Print Table button, you'll see no records in the output. Adding a call to the dataset's First method before calling the producer component's Content method fixes the problem.

To make the output of this producer component more complete, you can perform two operations. The first is to provide some Header and Footer information (to generate the HTML heading and closing elements) and add a Caption to the HTML table. The second is to customize the table by using the setting specified by the RowAttributes, TableAttributes, and Columns properties. The property editor for the columns, which is also the default component editor, allows you to set most of these properties, providing at the same time a nice preview of the output, as you can see in Figure 19.9. Before using this editor, you can set up properties for the dataset's fields using the Fields editor. This is how, for example, you can format the output of the population and area fields to use thousands separators.

Click To expand
Figure 19.9:  The editor of the DataSetTable-Producer compo-nent's Columns property provides you with a preview of the final HTML table (if the data-base table is active).

You can use three techniques to customize the HTML table, and it's worth reviewing each of them:

  • You can use the table producer component's Column's property to set properties, such as the text and color of the title, or the color and the alignment for the cells in the rest of the column.

  • You can use the TField properties, particularly those related to output. In the example, I've set the DisplayFormat property of the ClientDataSet1Area field object to ###,###,###. This is the approach to use if you want to determine the output of each field. You might go even further and embed HTML tags in the output of a field.

  • You can handle the DataSetTableProducer component's OnFormatCell event to customize the output further. In this event, you can set the various column attributes uniquely for a given cell, but you can also customize the output string (stored in the CellData parameter) and embed HTML tags. You can't do this using the Columns property.

In the HtmlProd example, I've used a handler for this event to turn the text of the Population and Area columns to bold font and to a red background for large values (unless it is the header row). Here is the code:

procedure TFormProd.DataSetTableProducer1FormatCell(
  Sender: TObject; CellRow, CellColumn: Integer;
  var BgColor: THTMLBgColor; var Align: THTMLAlign;
  var VAlign: THTMLVAlign; var CustomAttrs, CellData: String);
  if (CellRow > 0) and
    (((CellColumn = 3) and (Length (CellData) > 8)) or
    ((CellColumn = 4) and (Length (CellData) > 9))) then
    BgColor := 'red';
    CellData := '<b>' + CellData + '</b>';

The rest of the code is summarized by the settings of the table producer component, including its header and footer, as you can see by opening the source code of the HtmlProd example.

Using Style Sheets

The latest incarnations of HTML include a powerful mechanism for separating content from presentation: Cascading Style Sheets (CSS). Using a style sheet, you can separate the formatting of the HTML (colors, fonts, font sizes, and so on) from the text displayed (the content of the page). This approach makes your code more flexible and your website easier to update. In addition, you can separate the task of making the site graphically appealing (the work of a web designer) from automatic content generation (the work of a programmer). Style sheets are a complex technique, in which you give formatting values to the main types of HTML sections and to special "classes" (which have nothing to do with OOP). Again, see an HTML reference for the details.

You can update table generation in the HtmlProd example to include style sheets by providing a link to the style sheet in the Header property of a second DataSetTableProducer component:

<link rel="stylesheet" type="text/css" href="test.css">

You can then update the code of the OnFormatCell event handler with the following action (instead of the two lines changing the color and adding the bold font tag):

CustomAttrs := 'class="highlight"';

The style sheet I've provided (test.css, available in the source code of the example) defines a highlight style, which has the bold font and red background that were hard-coded in the first DataSetTableProducer component.

The advantage of this approach is that now a graphic artist can modify the CSS file and give your table a nicer look without touching its code. When you want to provide many formatting elements, using a style sheet can also reduce the total size of the HTML file. This is an important element that can reduce download time.

Dynamic Pages from a Custom Server

The HtmlProd component can be used to generate static HTML files; it doubles as a web server, using an approach similar to that demonstrated in the HttpServ example, but in a more realistic context. The program accesses the request of one of the possible page producers, passing the name of the component in a request. This is a portion of the IdHTTPServer component's OnCommandGet event handler, which uses the FindComponent method to locate the proper producer component:

  Req, Html: String;
  Comp: TComponent;
  Req := RequestInfo.Document;
  if Req [1] = '/' then
    Req := Copy (Req, 2, 1000); // skip '/'
  Comp := FindComponent (Req);
  if (Req <> '') and Assigned (Comp) and
    (Comp is TCustomContentProducer) then
    Html := TCustomContentProducer (Comp).Content;
  ResponseInfo.ContentText := Html;

In case the parameter is not there (or is not valid), the server responds with an HTML-based menu of the available components:

Html := '<h1>HtmlProd Menu<h1><p><ul>';
for I := 0 to ComponentCount - 1 do
  if Components [i] is TCustomContentProducer then
    Html := Html + '<li><a href="/' + Components [i].Name + '">' +
      Components [i].Name + '</a></li>';
Html := Html + '</ul></p>';

Finally, if the program returns a table that uses CSS, the browser will request the CSS file from the server; so, I've added some specific code to return it. With the proper generalizations, this code shows how a server can respond by returning files, and also how to indicate the MIME type of the response (ContentType):

if Pos ('test.css', Req) > 0 then
  CssTest := TStringList.Create;
    CssTest.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.css');
    ResponseInfo.ContentText := CssTest.Text;
    ResponseInfo.ContentType := 'text/css';

Part I: Foundations