

                
               ***********************************************
               *                                             *
               * A GUIDE TO FOXPRO/WINDOWS PRINTING BEHAVIOR *
               *                                             *
               ***********************************************

                                 Brad Schulz
                                 PO Box 383
                             San Carlos, CA 94070
                                415-593-3224

                                CIS 76640,152
                                
                                
This document comes about as a result of lots of experimentation and testing,
not to mention the devastation of entire forests with all the paper that I've
used.  I hope you find it useful.  Any comments would be welcome, as well as
any observations that you may have discovered yourself.

Please note that the results outlined in this document came about with the
Windows Printer Manager in effect.  I have done no testing with the Print
Manager disabled.

First, a little background:

DOS has the following reserved device names:
  
  AUX,COM1,COM2,COM3,COM4   Serial Ports (AUX is the same as COM1)
  PRN,LPT1,LPT2,LPT3        Parallel Ports (PRN is the same as LPT1)
  CON                       The Console (Screen)
  NUL                       Null Port (Nowhere/ByteHeaven)
      
These device names are also reserved file names.  Therefore if you execute the
DOS statement ECHO HELLO > LPT1, the word "HELLO" would appear on your
printer; it would not create a filename called LPT1.  The same is true if you
were to re-write the statement as ECHO HELLO > E:\MYDIR\LPT1.DUM.  No file
would be created on your E Drive; the output would go to the printer.

FoxPro (like any other application that uses the standard BIOS routines) will
do the same thing.  So the command LIST TO FILE E:\MYDIR\LPT1.DUM will list
your database directly to your parallel port.  You can get the same result by
issuing the commands SET PRINTER TO E:\MYDIR\LPT1.DUM and then LIST TO PRINTER.

You'll see that FoxPro for Windows (FPW) does not always exhibit this same
behavior.

The examples contained in this document use certain Report/Label names.  The
descriptions are as follows:

  DOSDEF    A Report/Label file containing only DOS objects
  WINDEF    A Report/Label file containing Windows objects.  There can
             be DOS objects in the file also (in other words, the Report or
             Label file has been through the TRANSPORT program).

Also in this document, I refer to a "PDSETUP being active".  This refers to
a Printer Driver Setup created through the FoxPro/DOS GENPD.APP.  There
are several ways a PDSETUP can be active:

  1. You choose a PDSETUP through the File Menu's Printer Setup option.
  2. You type SET PDSETUP TO <something> (where <something is not "").
  3. You type _PDSETUP=<something> (where <something> is not "").
  4. Your CONFIG.FP has a line in it that says PDSETUP=<something> when
      you start FoxPro (where <something> is not "" or "-").
  5. Your CONFIG.FP has no PDSETUP line in it when you start up FoxPro
      but you have in a prior FoxPro session set up one of your PDSETUPs
      to be the default.
  6. You issue a REPORT or LABEL command with the PDSETUP clause and you
      had checked the "[ ] Printer Driver Setup" check-box and chose a
      PDSETUP when you designed the Report or Label layout.
  
Please note what happens, though, when you execute the following:

  SET PDSETUP TO "My LaserJet Setup"
  *
  * After the above, you are ready to print in CG Times on a LJIII
  *
  REPORT FORM <report with no PDSETUP defined> TO PRIN PDSETUP
  *
  * Since the Report has no PDSETUP defined, issuing the PDSETUP clause
  * above will make FoxPro assume you want no PDSETUP active while
  * printing the report, so it "closes" your LaserJet Setup and no
  * PDSETUP is active during (or after) the report
  
This can be a "gotcha".

There are also times in this document when I refer to the SET PRINTER FONT.
There is an undocumented FPW command that was added too late to be included
in any printed or on-line documentation:

  SET PRINTER FONT <expC1> [,<expN1>] [STYLE <expC2>]
  
where <expC1> is the font name, <expN1> is the font size in points (default
is 10 points), and <expC2> is the font style (default is standard style).
Once you issue this command, any subsequent printed output to your printer
will use that font definition (unless overridden by a FONT/STYLE clause that
some commands offer.  Also note that Reports/Labels with Windows objects
will print their objects as defined in the Report/Label definition and not
use the SET PRINTER FONT).  So, for example:

  SET PRINTER FONT "Arial",14
  LIST STATUS TO PRINTER
  
The above will do a print in 14-point Arial.  If no SET PRINTER FONT is in
effect, then FPW prints in 10-point FoxPrint.  A SET PRINTER FONT command
stays in effect until either another SET PRINTER FONT command is issued or a
SET PRINTER TO command is issued (which sets it back to the default of 10-point
FoxPrint).

I should mention that at the current time (May1993), the STYLE clause of
this command does not work.  FPW ignores it.  Microsoft is aware of the
problem and hopefully it will be fixed in a future update.

Now you're ready to read the attached information...

LIST...
DISPLAY...
DIR...
TYPE...
SELECT...
*******************************************************************************

The above commands offer a TO FILE or a TO PRINTER clause.  Let's use the LIST
command in the following examples.  All of the other commands above will behave
the same way:

--------------------------
LIST TO FILE <destination>
--------------------------

Behavior: 
  FPW prints to the <destination> in character mode.  If a PDSETUP is active,
  then it takes effect.

Examples:
  LIST TO FILE LPT1
    Outputs directly to the parallel port.
  LIST TO FILE C:\SUBDIR\LPT1.DUM
    Does the same thing.
  LIST TO FILE E:\TEST\MYFILE.TXT
    Outputs to that filename.

Conclusions:
  Normal behavior.  The same happens in FoxPro/DOS.  It should be noted,
  though, that you can get nice fast character-based output to your printer
  in FPW by using the command LIST TO FILE LPT1.

----------------------------
SET PRINTER TO <destination>
LIST TO PRINTER
  or
SET PRINTER TO <destination>
SET PRINTER ON
LIST
----------------------------

Behavior: 
  Depends on whether a PDSETUP is active or not.  If one is active, then FPW
  prints to the <destination> in character mode, using the PDSETUP.  However,
  if a PDSETUP is *not* active, then what happens depends on the <destination>.
  If <destination> starts with the characters "PRN" or "LPT", then FPW ignores
  the destination and instead prints to the port defined for the currently-
  active Windows Printer Driver using the current SET PRINTER FONT.  Otherwise,
  it prints to the <destination> in character mode.  The following pseudo-code
  will illustrate this more clearly:
 
LIST...
DISPLAY...
DIR...       (Continued)
TYPE...
SELECT...
*******************************************************************************

     IF PDSETUP Active
       Print to <destination> in character mode using the PDSETUP
     ELSE
       IF <destination> starts with "PRN" or "LPT"
         Ignore the destination and instead print to the port
         defined for the currently-active Windows Printer Driver
         with the output being printed in the current SET PRINTER FONT
         (In other words, the output is graphically created as a rule)
       ELSE
         Print to <destination> in character mode
       ENDIF
     ENDIF
     
Examples:
  SET PRINTER TO LPT2 with PDSETUP Active
    Outputs to the LPT2 port in char mode using the PDSETUP
  SET PRINTER TO LPT2 with no PDSETUP Active
    Ignores the LPT2 port and instead prints to the port defined for the
    current Windows Printer Driver using the current SET PRINTER FONT
  SET PRINTER TO E:\MYDIR\LPTTEST.DUM with PDSETUP active
    Outputs to that filename using the PDSETUP
  SET PRINTER TO E:\MYDIR\LPTTEST.DUM with no PDSETUP active
    Ignores the filename stipulated and instead prints to the port
    defined for the current Windows Printer Driver using the current
    SET PRINTER FONT
  SET PRINTER TO C:\TEST\TEST.TXT with PDSETUP Active
    Outputs to that filename in char mode using the PDSETUP
  SET PRINTER TO C:\TEST\TEST.TXT with no PDSETUP Active
    Outputs to that filename in character mode

Conclusions:
  If you have no PDSETUP active, then you can't print to a filename that
  starts with "PRN" or "LPT".  You also cannot print directly to a parallel
  port.  However, this obstacle can be overcome by using the TO FILE clause
  instead.  FoxPro/DOS (of course) does not have the same behavior:  If you
  SET PRINTER TO PRN or LPT1 or LPT2 or LPT3 then output goes to the parallel
  port no matter what and destinations that start with "PRN" or "LPT" are
  considered legitimate filenames and output goes to a file.

REPORT FORM dosdef...
LABEL FORM dosdef...
*******************************************************************************

The above commands behave exactly like the commands on the previous page (LIST,
DISPLAY, etc).  Note that this is only because the Report/Label definition
contains DOS objects only.

It should be mentioned once again that if the Report/Label has no PDSETUP
internally defined for it, and one issues the REPORT/LABEL command with the
PDSETUP clause, then Foxpro "turns off" any PDSETUP that happens to be
currently active and then FPW will print the output using the currently-active
Windows Printer Driver.

REPORT FORM windef...
LABEL FORM windef...
*******************************************************************************

Note that these commands are acting on Report/Label definitions that contain
Windows objects.  We'll use the REPORT command as an example; the LABEL command
behaves the same way:

----------------------------------------
REPORT FORM windef TO FILE <destination>
----------------------------------------

Behavior:
  FPW prints to the <destination> using the currently-active Windows Printer
  Driver, printing the report and all its objects as designated in the
  Report definition (in other words, graphical output).  If the PDSETUP
  clause is included, it is ignored.  If any PDSETUP is active when the
  command is issued, it is ignored in favor of the Windows Printer Driver.
  
Examples:
  REPORT FORM windef TO FILE LPT3
    Outputs directly to the parallel port.
  REPORT FORM windef TO FILE E:\TEST\MYFILE.TXT PDSETUP
    Outputs to that filename.  The PDSETUP clause is ignored.

Conclusions:
  Nothing unexpected here.
  
-----------------------------
SET PRINTER TO <destination>
REPORT FORM windef TO PRINTER
  or
SET PRINTER TO <destination>
SET PRINT ON
REPORT FORM windef
-----------------------------

Behavior: 
  Acts the same as the TO FILE example stipulated above except for the
  fact that the <destination> is ignored completely.  FPW assumes that
  if you want to print TO PRINTER, then it's going to print to the printer
  that is defined by the currently-active Windows Printer Driver.  It will
  pay no attention to the <destination> you may have defined in the SET 
  PRINTER TO command.

Examples:
  SET PRINTER TO LPT2
  REPORT FORM windef TO PRINTER
    Ignores the LPT2 port and prints to the default Windows Printer Driver
  SET PRINTER TO D:\MYDIR\REPOFILE.TXT
  SET PDSETUP TO "My LaserJet Setup"
  REPORT FORM windef TO PRINTER
    Ignores the SET PRINTER TO filename and instead prints to the default
    Windows Printer Driver.  The active PDSETUP is ignored.
    
Conclusions:
  If you want to print a Report/Label definition that contains Windows objects,
  then you cannot designate the destination of your output through the SET 
  PRINTER TO command.  This can be overcome by using the TO FILE clause
  instead or else defining all the appropriate Windows Printer Drivers you
  may need for output to different ports so you can select the one you want.
  before printing.

SET PRINTER ON ... ?/??
SET DEVICE TO PRINTER ... @SAY
*******************************************************************************

The above commands exhibit the same behavior as the LIST TO PRINTER command
discussed earlier.  In other words:

     IF PDSETUP Active
       Print to <destination> in character mode using the PDSETUP
     ELSE
       IF <destination> starts with "PRN" or "LPT"
         Ignore the destination and instead print to the port
         defined for the currently-active Windows Printer Driver
         with the output being printed in the current SET PRINTER FONT
         (In other words, the output is graphically created as a rule)
       ELSE
         Print to <destination> in character mode
       ENDIF
     ENDIF

It is important to know that you will not see any output until you issue a
SET PRINTER TO command to close the print job.

It should be noted that if a PDSETUP is active, then the @SAY command does
not execute any part of it (which is also true in FoxPro/DOS).  The ?/??
command will execute the _PDRIVER's PDOBJECT() function with whatever STYLE
clause you may have stipulated being passed as a parameter to it.  The 
PDADVPRT() may also be called by a ?/?? command (and ? will cause PDLINEEND()
and PDLINEST() to execute also).

When printing in character mode, there is some strange behavior that goes on
when you mix output using *both* ?/?? and @SAY.  It seems that all of the
output from the ?/?? commands get collected into a pool of some sort and the
output from the @SAY's get collected in a separate pool.  When you issue the
SET PRINTER TO to close the print job, the pool of ?/?? output gets spit out,
followed by the pool of @SAY output.

It is interesting to note that even though this behavior is exhibited, the
?/?? command will still update the values of PROW() and PCOL() correctly.
Consider the following program:

     SET PRINTER TO MYFILE.TXT
     SET PRINTER ON
     SET DEVICE TO PRINTER
     ?
     ?? "This is on row "+LTRIM(STR(PROW()))
     @ 3,3 say "Row 3 Column 3"
     ?? " I'm on row "+LTRIM(STR(PROW()))
     ?
     ?? "Now I'm on row "+LTRIM(STR(PROW()))
     @ 6,10 say "Row 6 Column 10"
     ?? " This is on row "+LTRIM(STR(PROW()))
     ? 
     ?? "Now I'm on row "+LTRIM(STR(PROW()))
     @ 8,12 say "Row 8 Column 12"
     SET DEVICE TO SCREEN
     SET PRINTER OFF
     SET PRINTER TO
     RETURN
     

SET PRINTER ON ... ?/??             (Continued)
SET DEVICE TO PRINTER ... @SAY
*******************************************************************************

The program will give the following (expected) output in FoxPro/DOS:

     Actual Row  +---------------------------------------------
          0      |
          1      |This is on row 1
          2      |
          3      |   Row 3 Column 3 I'm on row 3
          4      |Now I'm on row 4
          5      |
          6      |          Row 6 Column 10 This is on row 6
          7      |Now I'm on row 7
          8      |            Row 8 Column 12     
                 +---------------------------------------------

The same program run in FPW will output the following garbage:

     Actual Row  +---------------------------------------------
          0      |
          1      |This is on row 1 I'm on row 3
          2      |Now I'm on row 4 This is on row 6
          3      |Now I'm on row 7
          4      |
          5      |  Row 3 Column 3
          6      |
          7      |         Row 6 Column 10
          8      |           Row 8 Column 12
                 +---------------------------------------------

What a mess, huh?  Actually, this can be solved by printing solely using @SAY
statements.  Just substitute all instances of "?" with "@ PROW()+1,0 SAY" and
all instances of "??" with "@ PROW(),PCOL() SAY".

Conclusions:
  Don't mix ?/?? and @SAY's when printing in character mode!!!
  If you have no PDSETUP active, then you can't print to a filename that
  starts with "PRN" or "LPT" or print directly to a parallel port.

???
*******************************************************************************

The ??? command was introduced into the FoxPro language so that you could
direct output directly to the printer without incrementing PROW() or PCOL().
In fact, you don't even need SET PRINT to be ON in order for it to work.

Behavior:
  I'm afraid I still haven't figured out all the weird things that this
  command does.  In general, what it seems to do is (sometimes) close whatever
  print job may currently be going on and then starts its own, directing
  output to the SET PRINTER TO <destination> in character mode.  However,
  if you're printing to a file and if any @SAY commands had been previously
  executed before the ???, then the ??? output and all subsequent ?/??/???
  output will disappear.
  
For some examples, consider the following program, called TESTQM3.PRG.  By the
way, in case you're wondering about why the WAIT WINDOW command is in here,
it's to give FPW time to (possibly) start printing the print job that the
first ??? command may have closed.
 
    PARAMETERS the_pdsetup,the_dest,atsay_flag
    SET PDSETUP TO the_pdsetup
    SET PRINTER TO &the_dest
    SET PRINTER FONT "Arial",30
    SET PRINTER ON
    IF atsay_flag
      SET DEVICE TO PRINTER
      @ 1,0 SAY "Here is an @SAY in row 1"
    ENDIF
    ? "Test Line 1"
    ??? "Now printing a triple"
    WAIT WINDOW "Just did the first ??? command.  Press a key"
    ? "Test Line 2"
    ??? "Another triple"
    ? "Test Line 3"
    ?
    IF atsay_flag
      @ 6,0 say "Another @SAY in row 6"
      SET DEVICE TO SCREEN
    ENDIF
    SET PRINTER OFF
    SET PRINTER TO
    RETURN
    
Now let's look at some examples using the above program (without @SAY's):

  DO TESTQM3 WITH "","LPT1",.F.
    Will output the following:
    +-----------------------------------------------
    |
    |Test Line 1                    (Printed in 30-point Arial)
    |***PAGE BREAK**********************************
    |Now printing a triple          (Printed in raw character mode)
    |Test Line 2Another triple
    |Test Line 3
    +-----------------------------------------------

???       (Continued)
*******************************************************************************

  DO TESTQM3 WITH "Epson-10cpi","LPT1",.F.
    Will output the following in character mode:
    +-----------------------------------------------
    |
    |Test Line 1Now printing a triple
    |Test Line 2Another triple
    |Test Line 3
    +-----------------------------------------------
  DO TESTQM3 with "","MYFILE1.TXT",.F.
  DO TESTQM3 with "Epson-10cpi","MYFILE1.TXT",.F.
    Will both output the following in character mode:
    +-----------------------------------------------
    |Another triple
    |Test Line 3
    +-----------------------------------------------
  DO TESTQM3 with "","MYFILE2.TXT ADDITIVE",.F.
  DO TESTQM3 with "Epson-10cpi","MYFILE2.TXT ADDITIVE",.F.
    Will both output the following in character mode:
    +-----------------------------------------------
    |
    |Test Line 1Now printing a triple
    |Test Line 2Another triple
    |Test Line 3
    +-----------------------------------------------

Now let's introduce some @SAY commands:

  DO TESTQM3 WITH "","LPT1",.T.
    Will output the following:
    +-----------------------------------------------
    |
    |Here is an @SAY in row 1       (Printed in 30-point Arial)
    |Test Line 1
    |***PAGE BREAK**********************************
    |Now printing a triple          (Printed in raw character mode)
    |Test Line 2Another triple
    |Test Line 3
    |
    |
    |
    |Another @SAY in row 6
    +-----------------------------------------------
  DO TESTQM3 WITH "Epson-10cpi","LPT1",.T.
    Will output the following in character mode:
    +-----------------------------------------------
    |
    |Test Line 1Now printing a triple
    |Test Line 2Another triple
    |Test Line 3
    |
    |Here is an @SAY in row 1
    |Another @SAY in row 6
    +-----------------------------------------------

???       (Continued)
*******************************************************************************

  DO TESTQM3 with "","MYFILE3.TXT",.T.
  DO TESTQM3 with "Epson-10cpi","MYFILE3.TXT",.T.  
  DO TESTQM3 with "","MYFILE4.TXT ADDITIVE",.T.
  DO TESTQM3 with "Epson-10cpi","MYFILE4.TXT ADDITIVE",.T.
    Will all output the following in character mode:
    +-----------------------------------------------
    |
    |Test Line 1
    |Here is an @SAY in row 1
    |
    |
    |
    |Another @SAY in row 6
    +-----------------------------------------------

Conclusions:
  What a fiasco!  The only scenario in which the ??? command works correctly
  in concert with *both* ?/?? and @SAY is when printing to a port (rather
  than a file) using a PDSETUP.  However, since printing using a PDSETUP is
  in character mode, the ?/?? and @SAY's do not print correctly in reference
  to each other (see ?/?? and @SAY pages in this document).
  The ??? command *does* work correctly with the ?/?? command alone in the
  scenario where you print to a File in ADDITIVE mode or when you print to
  a port with a PDSETUP.  Bottom line:  It looks like FPW treats the ???
  command as a purely DOS feature.

EJECT
*******************************************************************************

When _PADVANCE="FORMFEED", the EJECT command essentially just does a ? CHR(12).
Everything works fine with EJECT when running under a Windows Printer Driver.
However, when you have a DOS PDSETUP active and an EJECT executed just prior
to closing your print job, it gets lost.  Actually, it seems to sit around in
an internal buffer somewhere and gets output next time you start a new print
job.  Consider the following:

     _PADVANCE="FORMFEED"
     SET PDSETUP TO "Epson-10cpi"
     SET PRINTER TO MYFILE1.TXT
     EJECT
     SET PRINTER TO
     
After executing the above, MYFILE1.TXT is empty.  Now do the following:

     SET PRINTER TO MYFILE2.TXT
     SET PRINTER ON
     ? 
     SET PRINTER OFF
     SET PRINTER TO
     
Now MYFILE2.TXT contains a formfeed, followed by the carriage return and line
feed created by the ? command.

If you do an EJECT, it may be a good idea to execute the command ?? "" 
immediately afterwards.  This will force the form feed character to be output.

Since EJECT does a ? CHR(12), you shouldn't mix it with @SAY commands if you
are printing in character mode, because of the strange behavior exhibited by
mixing ?/?? and @SAY commands.  What happens is that all the formfeed characters
get printed first and *then* all the @SAY's.  I suggest you use a @ 0,0 in place
of an EJECT when doing @SAY output.

When _PADVANCE="LINEFEEDS", then all bets are off if you are printing in 
character mode.  There have been times when I've executed the following
statements 10 times in a row and gotten 10 different sets of file contents:

     _PADVANCE="LINEFEEDS"
     SET PDSETUP TO ""
     SET PRINTER TO MYFILE.TXT
     EJECT
     SET PRINTER TO
     
It seems that sometimes the print job closes too fast for all the linefeeds to
get into the file.

*******************************
* OTHER INTERESTING PHENOMENA *
*******************************
-------------------------------------------------------------------------------

The following phenomena are things I've discovered in playing with FPW during
the last few months.  Please note that they occur when printing *graphically*
(not character-based) in FPW.  Some of these and other phenomena are described
in greater detail in an article I've written that appears in the July 1993 issue
of FoxTalk from Pinnacle Publishing.

Unlike FP/DOS, FPW will always give you the true values of PROW() and PCOL().
If you output REPLICATE("*",200) to your printer in FP/DOS and then look at
the value of PCOL(), it will happily give you a value of 200, even though
your printer overflowed the output onto 2 or more lines.  In FPW, on the other
hand, PCOL() will give you the *actual* print head column position.  The same
is true with PROW().  If you output 100-or-so ? commands to your printer in 
FPD, it doesn't know that you've overflowed onto a new page, but FPW *does*
and the PROW() will accurately reflect the row position on the new page.

Not only will PCOL() and PROW() return true values, but they can be translated
into different font measurements as well.  Try the following:

  SET PRINTER ON
  SET PRINTER FONT "FoxPrint",10
  ? "This is a test"
  prow_before=PROW()    &&Returns 1
  pcol_before=PCOL()    &&Returns 14
  SET PRINTER FONT "Arial",12
  prow_after=PROW()     &&Returns 0.714
  pcol_after=PCOL()     &&Returns 15.556
  SET PRINTER OFF
  SET PRINTER TO
  
The numbers I showed in the comments above are the results on my printer.  The
numbers you get may be different.  The decimal portion of the PROW() and PCOL()
values are dependent on the resolution (dots/inch) of your printer.  For 
example, let's look at 10-point FoxPrint on a 300-dpi Laser Printer.  This
font has a 12 characters/inch pitch horizontally, which comes out to 
300/12=25 dots/character, so the most horizontal precision you can achieve
in column coordinates is 1/25=0.04.  By the same token, a 10-point character
is 10/72 inches tall (1 point=1/72 inch).  So 300*10/72=42 dots/character
vertically, and vertical precision is 1/42=0.0238.  If we analyzed the same
size character on a dot-matrix printer with 144x120 (vertical x horizontal)
dots/inch resolution, you could get no more precise than 0.05 units vertically
(20 dots) or 0.10 units horizontally (10 dots).

All that this precision stuff above means is that you can only place objects
on the printed page with a limited amount of accuracy, and that depends on 
your printer.  For example, if I attempt to position the 300-dpi laser print
head by issuing the command  @ 12.03,15.03  the print head will *actually* be
positioned at the closest dot coordinate, which is actually 12.0238,15.04.
On the 144x120 dot-matrix printer, the actual coordinates would end up being
12.05,15.00.  You can verify this by looking at the values of PROW() and
PCOL() when positioning your output via the @SAY command.

*******************************
* OTHER INTERESTING PHENOMENA *  (Continued)
*******************************
-------------------------------------------------------------------------------

The ? command has an interesting property:  It will always move the print head
to the next *integral* printer row.  Try the following code:

  SET PRINTER ON
  SET PRINTER FONT "Arial",10
  ? "Line 1"    &&Prints in 10-point Arial
  ? "Line 2"
  ? "Line 3"
  ? "Line 4" FONT "Arial",11
  ? "Line 5" FONT "Arial",11
  ? "Line 6" FONT "Arial",11
  SET PRINTER OFF
  SET PRINTER TO
  
You'll notice that the first 3 lines appear normal, but lines 4 through 6 are
spread apart vertically.  This is because FPW is advancing the print head to
the next integral *10-point Arial* row (since that is the active SET PRINTER
FONT).  Since 11-point Arial is slightly taller than 10-point Arial, the
? command is forced to do a line-feed to the next available 10-point row, which
ends up being 2 rows below the 11-point line.

The same thing happens if you mix fonts on the same line.  FPW somehow keeps
track of the tallest font and performs a line-feed far enough down for that
tallest font to be visible.  Output the following to your printer:

  ?  "10" FONT "FoxPrint",10
  ?? "12" FONT "FoxPrint",12
  ?? "30" FONT "FoxPrint",30
  ?? "20" FONT "FoxPrint",20
  ?  REPLICATE("X",20) FONT "FoxPrint",10
  
You will notice that the line of X's on that second line prints just below the
30-point characters of the first line.

By the way, this "integral row" phenomenon with the ? command also occurs when
outputting to the screen.



******************
* FINAL THOUGHTS *
******************
-------------------------------------------------------------------------------

If you want to print reliably in character mode in FoxPro/Windows, follow these
rules:

1) Use a PDSETUP.  If you don't use PDSETUPs, then either start using them or
   else use my Generic GENPD that I outline at the end of this document.
   
2) Create and edit all your Reports and Labels in FoxPro/DOS and don't
   transport them.  Of course, if you want to have your reports print
   graphically rather than character-based, then by all means create and
   edit them in FPW and/or use the transporter.
   
3) Don't mix output with the ?/?? and @SAY commands.  Either use all ?/?? in
   your job or all @SAY's.
   
4) As far as EJECT is concerned, issue a ?? "" immediately afterward to make
   sure that the formfeed character is output.  If you are creating output 
   using @SAY commands, then do an @ 0,0 rather than an EJECT.
   
5) If you want to use the ??? command, then only issue it in conjunction with
   ?/?? commands; don't use @SAY commands in the same print job.
   
6) Also with the ??? command, make sure you use the ADDITIVE keyword in your
   SET PRINTER TO destination.
   
7) If you're outputting data to your printer via ?/?? or @SAY commands, make
   sure you issue a SET PRINTER TO command so that FPW closes the print job
   and starts physically printing to your printer (or file).
   
In general, using a PDSETUP will solve the vast majority of your character-
based printing problems.

*****************
* GENERIC GENPD *
*****************
-------------------------------------------------------------------------------

If you don't usually use PDSETUPs, then outlined below is how to create a whole
GENPD application which you can use to force FPW to print everything in
character mode like FP/DOS.  Although it's essentially useless (redundant) in
FP/DOS, it will work in that environment also.

Step #1:  Create a file called GENRICPD.PRG:

  *
  * GENRICPD.PRG - Generic Printer Driver Setup Program
  *
  PARAMETERS calltype,pdname
  PRIVATE retval
  IF PARAMETERS()=0   &&Called from CONFIG.FP(W)
    calltype=0
    pdname=""
  ENDIF
  IF calltype=2       &&Called from FPD Report/Label Setup
    RETURN IIF(EMPTY(pdname),"Generic","")    &&Toggle
  ENDIF
  retval=""
  IF pdname="?"       &&Called from FPD File/Printer Setup Menu Option
    retval=IIF(EMPTY(_PDSETUP),"Generic","")  &&Toggle
  ELSE
    retval="Generic"
  ENDIF
  _PDRIVER=""         &&Close current Printer Driver (calls PDONUNLOAD)
  IF NOT EMPTY(retval)  
    PUBLIC _PDPARMS[3]
    _PDPARMS[1]="Generic"
    _PDPARMS[2]=.F.      &&PdPageEnd() just executed?
    _PDPARMS[3]=.F.      &&PdLineEnd() just executed?
    _PDRIVER="GENRICDV.PRG"  &&Open Printer Driver (calls PDONLOAD if exists)
  ENDIF
  _PDSETUP="-"+retval    &&The "-" prevents a recursive call to GENRICPD again
  RETURN retval

*****************
* GENERIC GENPD *  (Continued)
*****************
-------------------------------------------------------------------------------
       
Step #2:  Create a file called GENRICDV.PRG:

  *
  * GENRICDV.PRG - Generic Printer Driver Procedures/Functions
  *
  PROCEDURE PdOnUnload
    RELEASE _PDPARMS
  RETURN

  FUNCTION PdDocSt
  PARAMETERS height,width
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.F.
  RETURN ""

  FUNCTION PdPageSt
    PRIVATE ctlchars
    ctlchars=IIF(_PDPARMS[2],CHR(12)+CHR(13),"")
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.F.
  RETURN ctlchars

  FUNCTION PdPageEnd
    _PDPARMS[2]=.T.
    _PDPARMS[3]=.F.
  RETURN ""

  FUNCTION PdLineSt
    PRIVATE ctlchars
    ctlchars=IIF(_PDPARMS[3],CHR(13)+CHR(10),"")
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.F.
  RETURN ctlchars

  FUNCTION PdLineEnd
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.T.
  RETURN ""

  FUNCTION PdAdvPrt
    PARAMETERS here,there
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.F.
  RETURN IIF(here>there,CHR(13)+SPACE(there),SPACE(there-here))

  FUNCTION PdObject
  PARAMETERS theobj,objstyle
    _PDPARMS[2]=.F.
    _PDPARMS[3]=.F.
  RETURN theobj
  
Step #3:  Create a Project called GENRICPD.PJX that contains the 2 programs
          GENRICPD.PRG and GENRICDV.PRG, making GENRICPD.PRG the "Main"
          Program.  Then Build the Project into an APP called GENRICPD.APP.

*****************
* GENERIC GENPD *  (Continued)
*****************
-------------------------------------------------------------------------------

You can put this Generic PDSETUP into place by executing the following two
commands:

  _GENPD="GENRICPD.APP"
  _PDSETUP="Generic"
  
You may need to fully qualify the GENRICPD.APP in the above _GENPD line if
the APP is in a different directory.  You can either execute the above 2
commands from within FPW or put the following 2 lines into your CONFIG.FPW so
that the Generic PDSETUP is in effect every time you start FPW:

  _GENPD="GENRICPD.APP"
  PDSETUP="Generic"

By the way, it's interesting to note that only the _GENPD="GENRICPD.APP" line
is required in your CONFIG.FP if you are starting FoxPro/DOS.  FoxPro/DOS
automatically executes the _GENPD application on startup; whereas, FPW does
not.  That is why you must add the PDSETUP="Generic" to your CONFIG.FPW.

Because of the way I've written GENRICPD.PRG, you can put almost any string
into the _PDSETUP system variable and it will cause the PDSETUP to be in
effect (and will set the system variable _PDSETUP to "Generic" anyway).  The
exceptions are:

  _PDSETUP=""      &&Turns the PDSETUP off
  _PDSETUP="?"     &&Toggles the PDSETUP on/off
  _PDSETUP="-"+<any string>  &&Does not actually execute the GENRICPD.APP

The reason for the "?" toggle is because of how FoxPro/DOS calls the _GENPD
application from its File Menu's Printer Setup Option.  And, as outlined in
the FoxPro Printer Driver documentation, setting _PDSETUP to any string that
starts with a dash ("-") will set the _PDSETUP system variable to the portion
of the string after the dash, but will not actually execute the _GENPD
application.

Once you have the Generic PDSETUP active, then all printer output in FPW will
be just like printing without a PDSETUP in FoxPro/DOS, as long as you follow
the rules I stipulated in the "Final Thoughts" section of this document.

Good luck!
