Creating PDF Files from ISAPI DLLs
Posted: (EET/GMT+2)
Creating PDF Files from ISAPI DLLs
November 24, 2001
Delphi 6 Professional or greater
Adobe's Portable Document Format (PDF) has been hugely successful in the recent years. Almost any web site offering product datasheets, literary or the like is delivering the information in PDF format. Since you don't want to miss this game, read this article to learn how to create PDF files from your Borland Delphi web applications.
To join the PDF revolution, you first need to understand the PDF file format. At present, there are multiple versions of the format, but simplistically thinking it is a "binary" text file format that is derived from PostScript. A PDF file contains "objects" that contain information displayed on the "pages" of the PDF document. To maintain device independence, all coordinates etc. are virtualized.
The following is an example of a really simple PDF 1.0 document file that displays the string "Hello!":
%PDF-1.0 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /Resources << /Font << /MyFont 5 0 R >> >> /MediaBox [0 0 284 200] /Contents 4 0 R >> endobj 4 0 obj << /Length 48 >> stream BT /MyFont 16 Tf 100 100 Td (Hello!) Tj ET endstream endobj 5 0 obj << /Type /Font /Subtype /Type1 /Name /MyFont /BaseFont /Helvetica /Encoding /StandardEncoding >> endobj xref 0 6 0000000000 65535 f 0000000010 00000 n 0000000067 00000 n 0000000133 00000 n 0000000274 00000 n 0000000378 00000 n trailer << /Size 6 /Root 1 0 R >> startxref 502 %%EOF
Of course, going through all the details of a PDF document would take too much space here. If you are interested in the details, take a visit at the Adobe Solutions Network page, and from there navigate to the Acrobat 5.0 SDK documentation page. For there you should be able to find a document for PDF file format, of course in PDF file format itself.
You can also find lots of useful PDF resources at PDFzone.com.
Creating PDFs on the fly
If you want to create PDFs in your Delphi web applications, you simply need to construct a PDF file dynamically and then return it to the browser. If you simply want to write text in your PDF, this is quite easy to achieve. For example, in the case of the sample application, there are only about 250 lines of code.
So, lets see how the sample application is built. It is a very basic ISAPI application with just one action, supporting the POST HTTP method (verb as some call them). In essence, the main action code is the following:
procedure TPDFWriterWebModule.PDFWriterWebModuleCreatePDFActionAction(
Sender: TObject; Request: TWebRequest; Response: TWebResponse;
var Handled: Boolean);
begin
{ get parameters from HTML form }
PageWidth := Trim(Request.ContentFields.Values['pagewidth']);
PageHeight := Trim(Request.ContentFields.Values['pageheight']);
TextXPos := Trim(Request.ContentFields.Values['textxpos']);
TextYPos := Trim(Request.ContentFields.Values['textypos']);
FontSize := Trim(Request.ContentFields.Values['fontsize']);
WrapColumn := Trim(Request.ContentFields.Values['wrapcolumn']);
TextString := Trim(Request.ContentFields.Values['textstring']);
{ create the PDF }
PDFData := PDFHeader;
WriteCatalogAndPageHeader;
WritePageAndContent;
WriteCrossReferences;
WriteTrailer;
{ return it to the browser }
Response.ContentType := 'application/pdf';
Response.CustomHeaders.Add('Content-Disposition=attachment; '+
'filename="dynamic.pdf"');
Response.Content := PDFData;
end;
Here, the code reads several parameters from an HTML form (see below) and then starts to create the PDF and store the data in a simple string variable. Once the creation process in finished, the data is outputted to the browser. Note how the content type is set to "application/pdf" and how the default filename is set using the "Content-Disposition" HTTP header (see RFC 2183 for details and security issues related to this header).
For your enjoyment, here are a couple of declarations used by the code:
Const
CRLF = #13#10;
PDFHeader = '%PDF-1.0'+CRLF;
Type
TPDFWriterWebModule = class(TWebModule)
procedure PDFWriterWebModuleCreatePDFActionAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
{ Private declarations }
PageWidth : String;
PageHeight : String;
TextXPos : String;
TextYPos : String;
TextString : String;
FontSize : String;
PDFData : String;
WrapColumn : String;
Function MMToUserUnit(Value : String) : String;
Function IntToPDFRef(Value : Integer) : String;
Function MyWrapText(Line : String; MaxCol : Integer) : String;
Procedure WriteCatalogAndPageHeader;
Procedure WritePageAndContent;
Procedure WriteCrossReferences;
Procedure WriteTrailer;
public
{ Public declarations }
end;
To feed parameters to the DLL, an HTML form like the following is used:

It isn't the prettiest HTML form around, but does the job since the focus on PDF creation.
Writing page data
The most important function in creating the PDF textual data is shown below:
procedure TPDFWriterWebModule.WritePageAndContent;
Var
YPos,Stream,Line : String;
Row,LineLen : Integer;
SL : TStringList;
begin
{ PDF coordinates start from left-bottom instead of left-top }
YPos := IntToStr(StrToInt(PageHeight)-StrToInt(TextYPos));
Stream := 'BT'+CRLF+
'/MyFont '+FontSize+' Tf'+CRLF+
MMToUserUnit(TextXPos)+' '+
MMToUserUnit(YPos)+' Td'+CRLF+
FontSize+' TL'+CRLF;
SL := TStringList.Create;
Try
LineLen := StrToInt(WrapColumn);
If (LineLen > 0) Then TextString := MyWrapText(TextString,LineLen);
SL.Text := TextString;
For Row := 0 to SL.Count-1 do Begin
Line := Trim(SL[Row]);
Line := StringReplace(Line,'(','\050',[rfReplaceAll]);
Line := StringReplace(Line,')','\051',[rfReplaceAll]);
Stream := Stream+'('+Line+') Tj T*'+CRLF;
End;
Finally
SL.Free;
End;
Stream := Stream+'ET'+CRLF;
PDFData := PDFData+
'4 0 obj'+CRLF+
'<< /length '+inttostr(length(stream))+' >>'+CRLF+
'stream'+CRLF+
Stream+
'endstream'+CRLF+
'endobj'+CRLF+
CRLF+
{ font resource object "MyFont" }
'5 0 obj'+CRLF+
'<<'+CRLF+
'/Type /Font'+CRLF+
'/Subtype /Type1'+CRLF+
'/Name /MyFont'+CRLF+
'/BaseFont /Helvetica'+CRLF+
'/Encoding /StandardEncoding'+CRLF+
'>>'+CRLF+
'endobj'+CRLF+
CRLF;
end;
Here, the code first sets some text formatting parameters such as starting position and font, size and leading. Then, it starts to write the textual lines to the PDF file, inside an element called a "stream". Note that the text cannot contain parenthesis (among others special characters), and those are then escaped using an octal presentation such as \050.
Since the PDF file format doesn't actually resemble a Teletype machine with support for automatic word wraps, the sample application must handle long lines itself. With a bit help from the WrapText function (actually MyWrapText), the text can be fitted perfectly on the page. Note that the sample code uses a custom version of the SysUtils routine WrapText, since the default version of the function pays attention to quotes, both single and double. This is not what we want, so the sample code has disabled this feature using a trick called cut-n-paste programming.
The result
If you download the sample application code compile it, it should be ready to be executed by the HTML form. Open the HTML form in your browser, set the parameters (or accept the defaults) and click the button on the bottom. Your browser should either directly launch Acrobat Reader and display the created document or alternatively display the "Save As" dialog box to indicate that the resource cannot be displayed.
No matter which method you choose, the results should look something like this:
Of course, at present the text comes from a textarea HTML field, but it could come from a database or another web site or web service as well. Only your imagination is the limit!
Download the example code
Download creatingpdffilesfromisapidlls.zip (106 kB) which contains the sample ISAPI DLL application PDFWriter developed in this article. Please note that the application experience requires that you also have the free Adobe Acrobat Reader available to display the created document. However, this software itself is not required by the application.
* * *
Need consulting help to finish your web or PDF application? Drop us a line today!