Integrating fax and e-mail in your programs with iPRO and APRO
There you are, sitting back, looking at the Async Professional VCL and Internet Professional manuals on your bookshelf. "Hmmm", you say to yourself, "I wonder if these two component libraries can work together?" Or, "it sure would be cool if I could print to the APRO fax printer driver and give my client the chance to send the document by either fax or e-mail." Both libraries are communications libraries, they seem to have the right parts to put this together, and they have the same last name, so it should be easy enough to do. Well, it turns out that it is pretty easy, and we'll go over all the parts right after we have the obligatory market-speak about our products.

Async Professional VCL (APRO for short) is an award winning serial communications library. Areas where it excels are protocol file transfers, TAPI, and faxing, among hundreds of other possibilities. Internet Professional (iPRO for short) carries on this long tradition by providing Internet standard protocols, including SMTP, NNTP, POP3, FTP client components and a pure VCL HTML panel.

OK, so we know APRO does faxing (and does it rather well), and iPRO can do the e-mail part. How can we fit them together? Let's begin at the beginning, which is always the best place to begin.

Faxing with Async Professional
Faxing with APRO is pretty straightforward. APRO's fax printer driver creates APF files (a kind of graphics file) which are then faxed using the APRO faxing components.

APRO's TApdFaxDriverInterface component provides notification when a print job starts and ends. From the component's OnDocEnd event handler you can fax the document. The bare minimum that APRO needs to know to send a fax is which modem to use, what fax file to send, and where to send it. Let's try an example. Go ahead and create a new project with Borland Delphi. We're going to set up the faxing side of our little example first, then integrate the e-mail part later.

If you were to compile and run this now, you would have something that intercepted fax printer print jobs, obtained information from the user about where to send the fax, and sent the fax. Hmmm, you've just replaced WinFax.

From the APRO tab of the component palette drop one of each of the following components on to the main form of your new project: TApdFaxDriverInterface, TApdSendFax, TApdFaxStatus, TApdComPort, and TApdTapiDevice. Those few components are all you'll need from APRO so switch over to the Standard tab of Delphi's component palette.

Remember the bare minimum that we need to fax? It's knowing what file to send, which modem to use, and where to send the file. We already have the fax file's information because the fax printer driver is going to create the APF file for us and we will be able to get its name from the TApdFaxDriverInterface.FileName property. Now we need to be able to select a modem to fax with, and to be able to get the phone number to fax to.

Drop two TLabels, one TComboBox, and one TEdit on the form and arrange them in a somewhat logical manner. Change the caption for the TLabel closest to the TComboBox to "TAPI device", and the caption for the other TLabel to "Phone number". We want to provide the user with a list of installed TAPI devices so they can chose the device they want to use. Create the form's OnCreate event handler and do something like this:

procedure TForm1.FormCreate(Sender: TObject);
  ComboBox1.Text := ApdTapiDevice1.TapiDevices[0];

The first line of this code puts the names of all the installed TAPI devices into the drop-down list. The second line of code pre-selects the device that is listed first.

When a document is printed to the APRO fax printer driver, the TApdFaxDriverInterface generates an OnDocStart event when the print job starts and an OnDocEnd event when the print job is complete. The TApdFaxDriverInterface.FileName property indicates where the printer driver saved the newly created APF file, which we can now fax. To keep our example simple, we just used the default value for FileName ("C:\DEFAULT.APF"). We'll also show a simple progress message in the form's Caption.

procedure TForm1.ApdFaxDriverInterface1DocStart(Sender: TObject);
  Caption := 'Printing started';

From the OnDocEnd event handler (which is generated to indicate when the print job has ended), we might be inclined to start the fax, but first we need to make sure we have the phone number and TAPI device selection from the user first. Rather than blindly starting the fax, let's drop a TButton on the form, change its Name property to 'btnFax', its Caption to 'Fax', and its Enabled property to False. When the OnDocEnd event is generated, we'll enable the button so that the user can enter the phone number to fax to and select the device to use. Your OnDocEnd event could look something like this:

procedure TForm1.ApdFaxDriverInterface1DocEnd(Sender: TObject);
  Caption := 'Done printing, enter information now.';
  btnFax.Enabled := True;

When the user clicks the 'Fax' button, we can set the appropriate properties and start the fax process. Since we're using TAPI, we will open the device in passthrough mode, start the fax from the TApdTapiDevice.OnTapiPortOpen event (which is generated only once the port has been configured and opened), and then close the port from the TApdSendFax.OnFaxFinish event. If you've been reading your APRO manual, you would have already entered something similar to this:

procedure TForm1.btnFaxClick(Sender: TObject);
{ the user said he's ready to fax, so commence faxing }
  { disable the Fax button while we are faxing }
  btnFax.Enabled := False;

  { select the device the user selected }
  ApdTapiDevice1.SelectedDevice := cbxTapiDevices.Text;

  { set the phone number }
  ApdSendFax1.PhoneNumber := edtPhoneNumber.Text;

  { use the Filename from the TApdFaxDriverInterface for the name 
    of the fax file to send }
  ApdSendFax1.FaxFile := ApdFaxDriverInterface1.FileName;

  { open the selected device in passthrough mode and wait for the 
    OnTapiPortOpen event }

procedure TForm1.ApdTapiDevice1TapiPortOpen(Sender: TObject);
{ TAPI opened the port, so we can start the actual fax now }

procedure TForm1.ApdSendFax1FaxFinish(CP: TObject; 
  ErrorCode: Integer);
{ the fax completed, close the TAPI device }
  ShowMessage('Fax complete: ' + ErrorMsg(ErrorCode));

If you were to compile and run this now, you would have something that intercepted fax printer print jobs, obtained information from the user about where to send the fax, and sent the fax. Hmmm, you've just replaced WinFax(tm). But you know that you'll need more. All we've done so far is send a fax. You can see it now. You'll add "now sends faxes" to the marketing blurb for you application and your customers will say, "So what? Everyone can send a fax!" What you really need is the e-mail integration we alluded to at the beginning of the article.

A short sidebar
But first, we need a little discussion about what we can - and can't - do with this setup. APRO's fax printer driver creates APF files, which, as we said earlier, are a type of graphics file. What else reads APF file? Well, it turns out that nothing. Only APRO can read APF files.

Because of this, when you e-mail an APF file you will need to be sure the receiver has an APF viewer they can use to see it. Ideally, this viewer will be associated with .APF files so that simply double-clicking on the e-mail attachment will launch your APF viewer. APRO includes components for viewing APF files so building a simple viewer applications is easy.

But what if you are going to send faxes to lots of different people and don't want to bother with distributing an APF viewer first? In this case you will need to convert the APF file into something that the receiver can view. In the example program we're building we'll take this approach and support sending the APF file as is, but also support converting the APF file to either BMP and TIFF formats, which are very popular and supported by a variety of third-party tools.

We now return to our article
Before we had our little diversion, we were able to detect when a print job started and when it ended. All we are really concerned about now is when the job ends so that we can either fax it or e-mail it.

Let's add another TButton to the form, name it 'btnEmail', change the Caption poroperty to 'E-mail' and set its Enabled property to False. This new button's OnClick event is where we will compose the message and send it so we will want to enable the button from the ApdFaxDriverInterface.OnDocEnd event (much like we did for the btnFax button). We're going to be doing a lot from the btnEmail.OnClick event handler, so go ahead and create the event handler now.

The btnEmailClick event is where we will gather the information required by Internet Professional's TIpSmtpClient component to send the message and where we'll gather the message itself. The easiest part of this is to get the SMTP server (the mail server) and other connection-related information.

To connect and logon to an SMTP server we will need the server's address, a user ID and a domain name. So, start by adding 3 sets of TLabels and TEdits. Name the TEdits edtUserID, edtSMTPServer, and edtDomain with corresponding TLabel captions. The beginning of the btnEmailClick event handler sets the appropriate IpSmtpClient properties so we could connect and logon. We also want to create a new message so we have something clean to work with.

We also need to know whom the message is from, to whom it is addressed, and a subject, so drop three more TLabel/TEdit combinations on the form. Name them edtMailFrom, edtMailTo and edtSubject. Assign the Text property of those TEdits to the TIpSmtpClient.Message.MailFrom, MailTo, and Subject properties, respectively. This is what we have so far:

procedure TForm1.btnEmailClick(Sender: TObject);
{ set up and transmit the doc via email }
  IpSMTPClient1.UserID := edtUserID.Text;
  IpSmtpClient1.Domain := edtDomain.Text;
  { make sure we have a clean message to work with }
  IpSmtpClient1.Message.UserFields.Add('X-IPro: Fax2Email');
  IpSmtpClient1.Message.From := edtMailFrom.Text;
  IpSmtpClient1.Message.Subject := edtSubject.Text;

Now we get to the fun part. We are going to add the APF file the printer driver just created (or the converted image you made) as a MIME attachment to this email. We'll also let the user add some text describing the fax image, so add a TMemo to the form and set its Name property to memMessage.

To add the MIME attachment we'll need to add a TIpMimeEntiry variable to the method. We're also going to add some plain text so that email readers that are not MIME-compliant will see something. To accommodate these, add both of these vars to the method:

procedure TForm1.btnEmailClick(Sender: TObject);
{ set up and transmit the doc via email }
  MimePart : TIpMimeEntity;
  Disclaimer : TStringList;

At the end of the event, we will encode whatever is in the memMessage and create the MIME disclaimer:

{ Add the memo text as a plain/text mime part }
MimePart := IpSmtpClient1.Message.GetBodyPlain(True);
MimePart.EncodeBodyStrings(memMessage.Lines, '');
IpSmtpClient1.Message.ContentType := 'multipart';
IpSmtpClient1.Message.ContentSubtype := 'mixed';
{ place mime disclaimer text in message body }
Disclaimer := TStringList.Create;
  with Disclaimer do begin
    Add('This message is in MIME format. '+ 
        'Since your mail reader does not');
    Add('understand this format, some or all of ' + 
        'this message may not be legible.');
  IpSmtpClient1.Message.EncodeBodyStrings(Disclaimer, '');

All that's left now is to actually attach the fax image and send it. Since we are going to support sending the image as the original APF or converted to the BMP or TIFF formats, go ahead and drop a TRadioGroup on the form and change its Caption property to 'Image format'. Edit its Items property so it has 'APF', 'Bitmap', and 'TIFF' options, and set the TRadioGroup's ItemIndex property to 0 so that the APF option is the default.

Handling the conversion
To convert the image to BMP or TIF we will use a TApdFaxUnpacker component. Drop one of those on the form now. The component's AutoScaleMode property determines how standard width APFs are scaled. To make the resulting converted image smaller, set it to asHalfWidth. The component's InFileName property is the input file (which is the fax file that the fax printer driver created). The OutFileName is the name you want for the converted image. All the conversion stuff can be done with this code:

{ convert the APF to a bitmap or TIFF, or leave it alone }
ApdFaxUnpacker1.AutoscaleMode := asHalfWidth;
ApdFaxUnpacker1.InFileName := ApdFaxDriverInterface1.FileName;
case rgpFormat.ItemIndex of
  0 : begin { sending APF }
    FaxName := ApdFaxDriverInterface1.FileName;
  1 : begin { sending bitmap }
    FaxName := ChangeFileExt(
      ApdFaxDriverInterface1.FileName, '.bmp');
    ApdFaxUnpacker1.OutFileName := FaxName;
  2 : begin { sending TIFF }
    FaxName := ChangeFileExt(
      ApdFaxDriverInterface1.FileName, '.tif');
    ApdFaxUnpacker1.OutfileName := FaxName;

OK, so the original fax file is now in the correct format (either left as an APF, or converted to a BMP or TIFF). To attach the file to the message just do this:

{ finally, add the converted fax image as a file attachment }

The message is now ready to send, so all that remains is to call the IpSmtpClient component's SendMail method to send it. The first parameter of SendMail is the address of the SMTP server. The second parameter determines whether the connection is terminated when the message is sent. In our project, the address of the SMTP server is provided to us in the edtSMTPServer TEdit, and we want to just send this one message and disconnect, so you'd want to have something like this as the last line in the btnEmailClick event handler:

  IpSmtpClient1.SendMail(edtSMTPServer.Text, True);


Wrapping up
We won't go into all of the intricacies of monitoring the progress of the SendMail method (see the SMTPDemo project that comes with Internet Professional if you want to know more about that). Instead, we will just use the TIpSmtpClient component's OnStatus event to show when a connection is made and when it is complete. Something like this would do nicely:

procedure TForm1.IpSmtpClient1Status(Sender: TObject; Socket: 
  Cardinal; Event: TIpStatusType; const Connection: TIpConnRec;
  const StatRec: TIpSockStatRec);
  if Event = stConnect then
    Caption := 'socket connected'
  else if Event = stDisconnect then
    ShowMessage('Email sent');

That wasn't too hard, was it? Now you can print a document to the APRO fax printer driver and either fax it with APRO or email it with iPRO. Not bad for a few minutes work!

The code behind this example is available on the TurboPower FTP server as in the folder.

This site is not affiliated, endorsed, or otherwise associated with the entity formerly known as TurboPower Software. The owners and maintainers of were merely meager employees of the aforementioned organization, providing this site out of the pure goodness of their collective hearts. Logo

Last updated: July 22, 2003.