Fax FAQ Table of Contents

  1. What is a fax?
  2. Fax hardware and software
  3. The APF file
  4. Creating APFs for faxing
  5. Converting APFs to other formats
  6. Doing other stuff with APFs
  7. Configuring for faxing
  8. Sending faxes
  9. Receiving faxes

What is a fax?

A fax is a graphic that is transmitted through phone lines from one station to another using a well-established protocol. Fax, which is short for facsimile, is not intended to transmit an exact copy of the original document being sent, instead it is supposed to provide an approximation of the original. The use of faxes has declined lately, in favor of the more flexible email, but it is still being used quite a bit.

Notice that I said that a fax is a graphic? I didn't say it was an text document, or that it sent something in the same format as the original. It's a graphic. The traditional fax machine (not a fax modem) will scan each line of the original document, convert it to a real bizarre format and transmit it to the receiver. The receiver will un-convert that format and (usually) reverse the scan an print each line onto a piece of paper. If you fax an MS Word document, you get a picture of that document on the other side, you will not get a copy of the original MS Word document. Am I clear enough? You'd be surprised to know how many people think faxes transmit exact copies of the original, down to the file name of the original document.

Back to Top

Fax hardware and software

As far as we're concerned, there are two types of fax hardware: a dedicated, stand-alone fax machine; and a PCs' faxmodem. APRO interacts with the faxmodem, a modem that also supports faxing. We do that via a few fax classes which we'll go into more detail a bit later. Almost all current modems are faxmodems, it's been a few years since I've seen one that didn't support fax.

Since the faxmodem is connected to the PC, and not to a physical printer, some kind of software needs to take the fax graphic's raster lines, convert it to something usable, and save the data to a file (or print it). The fax tab of APRO is filled with the components needed to send/receive/view/print and otherwise manage the faxmodem and fax files.

Back to Top

The APF file

APRO uses the APF format as it's native fax file format. The APF (Async Professional Fax) format is basically a G3 image wrapped up in a few headers. Each APF file starts off with a single TFaxHeaderRec record, followed by a TPageHeaderRec record preceding each page in the APF. As you may have surmised, the APF format can contain multiple pages in a single file, and they're laid out sequentially.  Here are the definitions of these records, copied from OOMisc.pas:

  {APRO fax file header record}
  TFaxHeaderRec = packed record
    Signature  : TSigArray;              {APRO FAX signature}
    FDateTime  : LongInt;                {Date and time in DOS format}
    SenderID   : Str20;                  {Station ID of sender}
    Filler     : Byte;                   {Alignment byte, unused}
    PageCount  : Word;                   {Number of pages in this file}
    PageOfs    : LongInt;                {Offset in file of first page}
    Padding    : Array[39..64] of Byte;  {Expansion room}
  end;
  {APRO fax page header record}
  TPageHeaderRec = packed record
    ImgLength : LongInt;                 {Bytes of image data in this page}
    ImgFlags  : Word;                    {Image flags for width, res, etc}
    Padding   : Array[7..16] of Byte;    {Expansion room}
  end;

As you can see, the TFaxHeaderRec has a Filler byte, and a Padding field, and the TPageHeaderRec has another Padding field, which are not explicitly used by anything in APRO. You are free to use those for your own purposes. The fax printer driver supports automatically setting the TFaxHeaderRec's Filler and Padding to something of your own choosing based on registry settings (BTW, that's new, new enough that it won't be available in a regular release until 4.06, but you can get it from here)

The thing to remember about the APF file format is that it is something that you can read using a TFileStream. Unless you have a G3 encoder/decoder, you probably can't do much with the page data itself, but all of the header info is there for your enjoyment. 

For the sake of conversation, let's say that you want to identify faxes that you've sent. You can read the TFaxHeaderRec from the APF and check the Filler byte before sending. If that's 0, then send the fax. Once you've sent it, open the APF, read the TFaxHeaderRec, change Filler to 1, then write the TFaxHeaderRec back to the APF. That will provide a very simple status of the fax file. Let's also say that we want to keep the phone number with the APF, we can use the Padding field to hold that. The TapiFaxInt.zip file from ftp://ftp.turbopower.com/pub/apro/demos/_Index.htm shows one implementation of this. Here's a real quickie that scans a folder for APFs, finds the ones marked as unsent and faxes them (if you use something like this, don't forget to add code to change the Filler byte once the fax is complete):

procedure TForm1.Button1Click(Sender: TObject);
var
  SR : TSearchRec;
  FS : TFileStream;
  Header: TFaxHeaderRec;
  FaxFile,
  PhoneNum : string;
begin
  FaxFile := '';
  FS := nil;
  // find the first fax marked as unsent
  if FindFirst('c:\faxes\*.apf', 0, SR) = 0 then begin
    repeat
      try
        FS := TFileStream.Create(SR.Name, fmOpenRead);
        FS.ReadBuffer(Header, SizeOf(TFaxHeaderRec));
        // if Filler is 0 we haven't sent it yet
        if Header.Filler = 0 then begin
          { move the phone number into Padding }
          Move(PhoneNum[1], Header.Padding, Length(PhoneNum));
          FaxFile := SR.Name;
          Break;
        end;
      finally
        FS.Free;
      end;
    until FindNext(SR) <> 0;
    FindClose(SR);
  end;
  // use the phone number from the Padding
  ApdSendFax1.PhoneNumber := PhoneNum;
  ApdSendFax1.FaxFile := FaxFile;
  ApdSendFax1.StartTransmit;
end;

 

Also, since the APF is an enhanced G3 image, you can create APFs out of G3 images and G3 images our of APFs. Simply add or remove the TFaxHeaderRec and TPageHeaderRec records as appropriate. If you have something that will create G3s, this can get real handy. Here are some snippets that show how:

procedure TForm2.btnCreateAPFClick(Sender: TObject);
var
  InFile, OutFile : TFileStream;
  FaxHeader : TFaxHeaderRec;
  PageHeader : TPageHeaderRec;
begin
  { creates an APF out of a G3 image }
  OpenDialog1.Filter := 'All files (*.*)|*.*';
  if not OpenDialog1.Execute then
    Exit;
  InFile := nil;
  OutFile := nil;
  try
    InFile := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);
    OutFile := TFileStream.Create(ChangeFileExt(OpenDialog1.FileName, '.apf'), fmCreate);
    FaxHeader.Signature := DefAPFSig;
    FaxHeader.FDateTime := 0;
    FaxHeader.SenderID  := 'SenderID';
    FaxHeader.PageCount := 1;
    FaxHeader.PageOfs   := SizeOf(TFaxHeaderRec);
    OutFile.WriteBuffer(FaxHeader, SizeOf(TFaxHeaderRec));
    PageHeader.ImgLength := InFile.Size;
    PageHeader.ImgFlags  := ffHighRes;
    OutFile.WriteBuffer(PageHeader, SizeOf(TPageHeaderRec));
    OutFile.CopyFrom(InFile, 0);
  finally
    InFile.Free;
    OutFile.Free;
  end;
end;
procedure TForm2.btnCreateG3Click(Sender: TObject);
var
  InFile, OutFile : TFileStream;
  FaxHeader : TFaxHeaderRec;
  PageHeader : TPageHeaderRec;
  FN : string;
  I : Integer;
begin
  { creates a G3 image out of an APF }
  OpenDialog1.Filter := 'Fax files (*.APF)|*.APF';
  if not OpenDialog1.Execute then
    Exit;
  FN := OpenDialog1.FileName;
  InFile := nil;
  OutFile := nil;
  try
    { open the APF, read the header }
    InFile := TFileStream.Create(FN, fmOpenRead);
    InFile.ReadBuffer(FaxHeader, SizeOf(TFaxHeaderRec));
    for I := 1 to FaxHeader.PageCount do begin
      { read each page header, extract the image data as a separate G3 image }
      InFile.ReadBuffer(PageHeader, SizeOf(TPageHeaderRec));
      try
        OutFile := TFileStream.Create(ChangeFileExt(FN, '.G3' + IntToStr(I)), fmCreate);
        OutFile.CopyFrom(InFile, PageHeader.ImgLength);
      finally
        OutFile.Free;
      end;
    end;
  finally
    InFile.Free;
  end;
end;
Back to Top

Creating APFs for faxing

The G3->APF conversion above is much more difficult than the usual ways of creating APFs. There are two ready-made APF creation paths: the TApdFaxConverter component and the fax printer driver.

 The TApdFaxConverter component can convert ASCII text, PCX, DCX, BMP, and TIFF files, and TBitmaps, into APF format. Simply set the DocumentName property to the name of the file to convert, and set the InputDocumentType property to the type of conversion, and call the ConvertToFile method. The resultant APF will be saved to the path\filename specified by the OutFileName property. Some things to know about TIFF: our TIFF support is pretty lame, we only support TIFF version 4, true monochrome, no compression, and no multi-page images (at the time the TApdFaxConverter was written, TIFF didn't have the concept of pages).

For any document that isn't in one of the above natively supported formats, either convert it to one of those formats, or print it to the fax printer driver. I don't want to hear how we should be supporting "Bob's Text Mangler Format", or even MS Word DOC formats. There is no way that we'd be able to support the hundreds of formatting commands embedded in an RTF document, much less a MS Word doc with an embedded Excel spreadsheet. Print the document to the fax printer driver. We'll talk a bit more about the printer driver later.

Back to Top

Converting APFs to other formats

An APF is a pretty good format, it's relatively compact and self-contained, and very good natured until it gets a few drinks. However, it seems that nothing can read APFs except for APRO (actually, QuickView can, but it renders the pages with a small amount of distortion). To use the APF, either one you've received via fax or have created, you'll want to convert it to another format. Since the APF is a graphic, you can only convert it to another graphic format. The TApdFaxUnpacker (it "unpacks" the APF into other formats) can unpack an APF into PCX, DCX, TIFF or Bitmap formatted files, or as a TBitmap. With the exception of DCX, none of these formats has the concept of "pages", so unpacking a multi-page APF will yield a single-page file. Notice that ASCII text is not in the list, APRO doesn't do OCR (Optical Character Recognition). There are a few component libraries available that will do the OCR thing against one of our supported formats.

To unpack an APF, set the TApdFaxUnpacker.InFileName property to the path\name of the APF to unpack, and OutFileName to the name of the file you want to create. Call one of the UnpackFileToXxx or UnpackPageToXxx methods to create the file.

Since most of our formats do not support pages, how would you handle that? It's pretty simple. Iterate from 1 to TotalPages and call UnpackPageToXxx for each page, setting OutFileName to a new filename for each page.

Back to Top

Doing other stuff with APFs

We've already talked about the APF format to some extent, now we can digress into stuff to manipulate APF files. Consider an APF that has multiple pages, but only one or two of them are relevant. The TApdFaxUnpacker.ExtractPage method can take a single page from an APF and extract it to a new single-page APF. If you need to extract more than one page, use ExtractPage for each page and create new temp files (each consisting of a single page), then use the TApdSendFax.ConcatFaxes method to create a single-multi-page APF from multiple-single-page APFs. Here's yet another quickie of doubtful use, it just extracts the odd pages from an APF and creates a new multi-page APF containing them.

procedure TForm1.Button1Click(Sender: TObject);
var
  I : Integer;
begin
  // select the file to work with
  ApdFaxUnpacker1.InFileName := 'e:\test cases\bigtext\warpeace.apf';
  // iterate through the pages
  for I := 1 to ApdFaxUnpacker1.NumPages do
    if Odd(I) then begin
      // extract the desired pages to a temp file
      ApdFaxUnpacker1.OutFileName := 'e:\test\Page' + IntToHex(I, 4) + '.apf';
      ApdFaxUnpacker1.ExtractPage(I);
      // add the temp file to the FaxFileList
      ApdSendFax1.FaxFileList.Add(ApdFaxUnpacker1.OutFileName);
    end;
  // we now have temp files of all the desired pages, and their names
  // in FaxFileList, concat them into a single APF
  ApdSendFax1.ConcatFaxes('e:\test\oddpages.apf');
  // delete the temp files
  for I := 0 to pred(ApdSendFax1.FaxFileList.Count) do
    DeleteFile(ApdSendFax1.FaxFileList[I]);
  ApdSendFax1.FaxFileList.Clear;
end;

Here's a variation of the above. In this one, the odd pages are converted to bitmaps, a big "X" is drawn on it, and the bitmap is converted back to a single-page temp APF. Even pages are extracted without modification. Finally, all of the temp pages are concatenated again.

procedure TForm1.Button1Click(Sender: TObject);
var
  I : Integer;
  BMP : TBitmap;
  TempFileName : string;
begin
  // select the file to work with
  ApdFaxUnpacker1.InFileName := 'e:\test cases\bigtext\warpeacelo.apf';
  // iterate through the pages
  for I := 1 to ApdFaxUnpacker1.NumPages do begin
    // add the temp file to the FaxFileList
    TempFileName := 'e:\test\Page' + IntToHex(I, 4) + '.apf';
    ApdSendFax1.FaxFileList.Add(TempFileName);
    if Odd(I) then begin
      // unpack the page to a TBirmap
      BMP := ApdFaxUnpacker1.UnpackPageToBitmap(I);
      // muck about with it
      BMP.Canvas.Pen.Width := 5;
      BMP.Canvas.Pen.Color := clBlack;
      BMP.Canvas.MoveTo(0, 0);
      BMP.Canvas.LineTo(BMP.Width, BMP.Height);
      BMP.Canvas.MoveTo(0, BMP.Height);
      BMP.Canvas.LineTo(BMP.Width, 0);
      // convert the bitmap back to a page in the APF
      ApdFaxConverter1.OutFileName := TempFileName;
      ApdFaxConverter1.ConvertBitmapToFile(BMP);
      BMP.Free;
    end else begin
      // extract the desired pages to a temp file
      ApdFaxUnpacker1.OutFileName := TempFileName;
      ApdFaxUnpacker1.ExtractPage(I);
    end;
  end;
  // we now have temp files of all the desired pages, and their names
  // in FaxFileList, concat them into a single APF
  ApdSendFax1.ConcatFaxes('e:\test\oddXpages.apf');
  // delete the temp files
  for I := 0 to pred(ApdSendFax1.FaxFileList.Count) do
  ApdSendFax1.FaxFileList.Clear;
end;

For all you you who wanted to put some kind of mark on a page how it can be done from the above snippet.

Back to Top

Configuring for faxing

If you're reading this sequentially, you're now the master of the APF format, and you're ready to fax something. APRO requires a properly configured faxmodem so it can do the fax thing. There are a few ways of gaining that illustrious goal. The easiest to use, both for your users and for you, is to use TAPI. In APRO 4, we integrated TAPI and the fax somewhat, so the fax component will do most of the work for you. To take advantage of this, set the TApdSendFax or TApdReceiveFax.TapiDevice property to an instance of a TApdTapiDevice. To automatically configure a device for faxing and send a fax, simply call the TApdSendFax.StartTransmit method and we'll take care of it. To automatically configure a device for faxing and wait for incoming faxes, simply call the TApdReceiveFax.StartReceive method.

One big advantage of using the TAPI/Fax integration when receiving faxes is that we'll wait around in the background for incoming calls, so the device can be used by other processes without having to turn the receiver off. If you're sending and receiving in the same process/app, you'll want to have a TApdTapiDevice for each fax component. If you use a single TApdTapiDevice for both the TApdReceiveFax and TApdSendFax, you will get exceptions when starting the faxes. As long as they are using separate TApdTapiDevice and TApdComPort components, you can happily wait for incoming calls while haphazardly sending faxes.

If you don't want to use TAPI, then use the TApdSendFax/TApdReceiveFax.ModemInit property to specify any special configuration required by the modem.

Back to Top

Sending faxes

Once you've configured the modem for faxing, send a fax by setting the TApdSendFax.FaxFile to the path\name of the APF to send, and the PhoneNumber property to the phone number of the fax machine to receive the fax. When you're ready to send, call the .StartTransmit method. As you can see, the bare minimum information that we need to send a fax is the name of the APF to send and where to send it.

After you call .Start Transmit, you'll start getting OnFaxStatus and OnFaxLog events. When the fax is complete, you'll get an OnFaxFinish (just ignore the OnFaxError event, it's redundant to the OnFaxFinish). The ErrorCode parameter of the OnFaxFinish event will tell you whether the fax was successful (ErrorCode = ecOK = 0), if it failed then ErrorCode <> 0.

The OnFaxFinish event is generated at the end of a fax session, which is not necessarily the same as a fax call. A fax session consists of everything from calling StartTransmit until you're done sending faxes. You can use the OnFaxNext event to specify several faxes to send to different recipients. When you call StartTransmit, we'll generate OnFaxNext if one is assigned. That's a pretty important statement, notice that I didn't say that we'll send the fax determined by FaxFile and PhoneNumber. If you have an OnFaxNext event, that takes precedence over those properties. As long as OnFaxNext returns with an APF to send and a phone number to send it to, we'll keep sending faxes.

Back to Top

Receiving faxes

Receiving faxes is just as easy as sending them. Call the TApdReceiveFax.StartReceive method, and any incoming faxes will be saved (in APF format) to the directory determined by the DestinationDir property, and named according to the FaxNameMode property. 

Like sending faxes, receiving faxes can consist of a single fax or multiple faxes in a single fax session. How we handle this is determined by the OneFax property. If that is True, we'll receive one fax and generate the OnFaxFinish event. If OneFax is False, we'll keep receiving faxes until either an error occurs or you call CancelFax, only then will we generate the OnFaxFinish event.

Back to Top