Mid-Michigan Computer Consultants - Bay City, Michigan

CONTENTS       (old style)
Mid-Michigan Computer Consultants
509 Center
Bay City, Michigan

Sales (989) 892-9242
Support (989) 686-8860

Plb-0270.cfm v1.0


ANSI Standard PL/B Language and Visual PL/B


This web resource is written for our own use. But we feel strongly that the PL/B language should be shared with the software community. So feel free to use this at will! BUT PLEASE... if you find errors or omissions or have a better way to do something. TELL US! Dialog helps us all. Send e-mail to:

VISUAL PL/B Language
A very powerful feature of Visual PL/B is the PRINT PAGE instruction. This instruction works very much like a DISPLAY to allow you to format a printed page randomly then print the entire page at one time.


Printer files are definded using the PFILE instruction. This is anologous to the FILE, IFILE and AFILE definitions for disk files. You'll open and close the printer file, much like a disk file.

Here is how we usually define a printer object (or file). Note that we also set up a couple of our own work areas.
PRINTER_FILE       PFILE             ;you MUST define the printer
PRINTER_NAME       DIM      31       ;we use this for optional name
SPOOL_ERROR        DIM       1       ;this is our own error flag
Before using the printer it must be opened with the PRTOPEN statement.

There are a number of parameters that you can use with the PRTOPEN. The minimum statement is:
     PRTOPEN  {pfile name}, {windows printer name}, {job name}
The windows printer name and the job name can both be null strings, but you must have SOMETHING in those positions. It can be a literal or a string variable name. For example you could code:
         PRTOPEN   PRINTER_FILE, "", ""
You could also code it like this:
         PRTOPEN   PRINTER_FILE, "HP Laserjet 4", ""
Or code it like this:
The printer name is very significant. If the name is a null string, or a null literal, then the windows printer selection dialog box will open and the user can select the printer.

The printer name string can also contain the name of an active and valid printer on the computer. If you do this, you have to be sure that you use a valid name of a printer that's actually installed on the computer where the program is running.

The generally accepted method is to leave the printer string NULL. This let's the user select the printer at run time.

In summary, the data in the windows printer name does these things:
  • "-" Tells the system to use the currently selected printer. (see PAGESETUP below).

  • "@" or "@printer name" or Does a "print preview" after you do a PRTCLOSE.

  • "@?" or "@?printer name" or You can tell the system to do a print preview, AFTER the print selection dialog, upon the PRTCLOSE

  • Of course you can also explicitly name the printer.
The standard Windows printer selection dialog includes a cancel button. If the user clicks that it will cause a runtime error unless you trap for it. The Sunbelt manual says you get an S22 but experience has shown that you actually get an S10. (At one time we had a note that you get an S18... can't prove that now.)

The trap to use for this is "SPOOL". Here's how we handled it:
         MOVE      NO,SPOOL_ERROR           ;this will be our flag
         TRAP      PRINTER_ERROR IF SPOOL   ;watch for an error
         PRTOPEN   PRINTER_FILE, "", ""     ;let user specify printer
         TRAPCLR   SPOOL                    ;clear the trap if not taken
         IF (SPOOL_ERROR != NO)             ;see if there was an error
         ..... could do something ...
             RETURN                         ;in our case we return
         ..... more program....
printer_error                               ;routine for the TRAP
         MOVE      YES,SPOOL_ERROR          ;just note error
         RETURN                             ;S$ERROR$ would have full data
The PAGESETUP statement provides another method for controlling the PRTOPEN

Normally the PRTOPEN with a null printer name will cause the Windows printer dialog to open, thus allowing the user to select the printer.

When you already KNOW the printer name, you can just provide it in the PRTOPEN and skip the dialog.

Sometimes the second open of the printer causes strange problems. For example, we've seen examples where the second PRTOPEN leaves Windows not knowing what type of printer is selected. After that, things like setting landscape orientation on a laser just won't work.

PAGESETUP selects a printer, and remembers what that printer is, without actually doing an open to the printer. Later you can do as many PRTOPENs as you want using the printer defined by the PAGESETUP.

We don't know where the PAGESETUP information is stored. One way to think about is that PAGESETUP puts the printer information onto the clipboard and PRTOPEN uses whatever is stored there.

PAGESETUP has no parameters. It's a standalone statement.

PAGESETUP opens the printer selection dialog on the options side, not on the printer selection side where you normally land. The user can set options, but your program can override them with "*" parameters in the PRTPAGE instruction.

Here's an example:
Suppose you want to provide a button for printing envelopes when the user selects a customer from a DATALIST.

On the button click you'd retreive the customer information then CALL a routine that does something like this:
         IF (FIRST_TIME = YES)
             MOVE  NO,FIRST_TIME

         PRTOPEN   PRINTER_FILE, "-",  ""
              ... do the envelope print....


The first time the routine is called, it does the PAGESETUP which establishes the printer. This only happens once.

Each time the routine is called it will open the printer file using the "-" printer name parameter which say to use the currently defined printer. The envelope is printed and the printer is closed which causes it to print immediately.

The user selects the printer only once then just gets an envelope each time the button is clicked.
Once a printer is opened, all output is sent to the Windows print handler. Nothing prints until you close the print file with PRTCLOSE. The format is simply:
    	     PRTCLOSE  PRINTER_FILE             ;close the printer
You can have multiple printer files open at any time. They can be opened and closed independently.

In the envelope example above, you could open a summary report printer file when the job starts and you'd print a single line to that report each time an envelope is printed.

The envelopes would print one at a time, with their printer file opening and closing each time.

At the end of the run, you'd close the summary report and it would produce a list of all the envelopes which had been printed.

The summary report job would be a good example of a place to use the "@" printer name option in the PRTOPEN. That option tells the printer select dialog to open after the CLOSE, not after the OPEN. It delays the printer selection to the END of the run.
The PRTPAGE instruction is a cross between a DISPLAY and a traditional PRINT instruction. Many of the list instructions are common to one format or the other. There is also a resemblence to a file I/O instruction in that you must name the printer. (You could have multiple printers open at the same time!)

To illustrate the similarity to a PRINT statement, note that you can code something like this:
         PRTPAGE   PRINTER_FILE;"This is data":
                      *N,"Here's the next line":
That prints two lines and "formfeeds" to the next page. Note that, like a file I/O routine, the printer file is named at the beginning and is followed by a semi-colon.

To illustrate how the page print is similar to a DISPLAY statement consider this example:
         PRTPAGE   PRINTER_FILE;*P10:10,*BOLDON,"position 10, line 10":
                      *P01:01,"back to top of page":
                      *P50:20,"farther down page"
There are numerous other commands including line and box drawing, font control, line control and the like.

GetFILE for printers
GETFILE {printer file}, PRTNAME=Printer_fullname, HDC=Printer_Handle, etc.

For most cases, you would use
GETINFO for printers. It gives the most information. One drawback is that it does not provide the full printer name. GETINFO gives just the first 31 bytes of the printer name. Many printers have much longer names!

GETFILE is supposed to give the full printer name. This requires PL/B version 9.x and above. Before that you still got the 31 byte name

GetINFO for printers
The GETINFO instruction can be used to retreive a lot of information about printers. There are several variations:
  • GETINFO PRINTERS,{string} (gets default printer name)
  • GETINFO PRINTERS,{datalist} (gets list of all printers)
  • GETINFO TYPE={printer object}, {string} (gets open printer specs)
GETINFO PRINTERS,{string} returns the DEFAULT printer name string. It doesn't matter what printer you have opened, this is the current DEFAULT printer name.

Note that the printer name which is returned may be more than you want, need or can use. For example you may get:
   HP LaserJet 4,HPPCL5MS,\\Gateway\1
If that occurrs, you want only the bytes up to the first comma:
   HP LaserJet 4

See special note in
printer management section dealing with network printers and printer name lengths.

GETINFO PRINTERS,{datalist} populates the {datalist} with a list of all the printers defined on the computer. This allows you to do your own printer selection dialog without actually opening a printer file.

Unlike the default printer format shown above, this format gives you only the printer name. You can use that name directly.

GETINFO TYPE={printer object}, {string} returns a predefined string defining the currently opened printer and just about everything about it.

The layout of the returned string is defined in the on-line help. We cut that table and pasted it into a file which we INCLUDE in our programs when we need the information. You can cut the table from this page if you want it. (Our include file is Z:PRT-INFO.PLS.)

WHERE in the on-line PL/B manual from Sunbelt? Go to the GETINFO index entry and click it. Then look for "PFILE Information" in the topic list. Click on it. You get:
.   This was extracted from the Sunbelt ON-LINE MANUAL 12/6/2000.
.   That was PLBWIN version 8.4
.   The GETINFO instruction retrieves the following information
.   when the TYPE keyword specifies a PFILE:
.   For example:
.              GETINFO  TYPE={printer file}, PRINTER_INFO_DATA
.    1.0 millimeter   = 0.039370078740157 inches
.   25.4 millimeters  = 1 inch
.    Column  Size    Value
.    1       8       Current Printer Driver version
.    9       8       Page width in pixels
.    17      8       Page height in pixels
.    25      8       Page width in millimeters
.    33      8       Page height in millimeters
.    41      8       Number of color bits per pixel
.    49      8       Number of device specific fonts
.    57      1       Can Print Pictures ? (Y or N)
.    58      4       User specified copies specified by user (future)
.    62      1       Print All Pages (Y or N)
.    63      1       Print Selection Only (Y or N)
.    64      8       Print dialog start page value
.    72      8       Print dialog end page value
.    80      1       Print Alignment Right (Y or N)
.    81      1       Print Alignment Decimal (Y or )
.    82      1       Fill On Enabled (Y or N)
.    83      1       Pict Rectangle Enabled (Y or N)
.    84      1       Overlay Enabled (Y or N)
.    85      4       Number of characters in Printer Name
.    89      31      Printer name
.    120     10      Print orientation as follows
.                    1 = Portrait
.                    2 = Landscape
.    130     10      Paper size as follows:
.                    1 = Letter, 8 1/2- by 11-inch
.                    2 = Letter Small, 8 1/2- by 11-inch
.                    3 = Tabloid, 11- by 17-inch
.                    4 = Ledger, 17- by 11-inch
.                    5 = Legal, 8 1/2- by 14-inch
.                    6 = Statement, 5 1/2- by 8 1/2-inch
.                    7 = Executive, 7 1/4- by 10 1/2-inch
.                    8 = A3 sheet, 297- by 420-mm
.                    9 = A4 Sheet, 210- by 297-mm
.                    10 = A4 small sheet, 210- by 297-mm
.                    11 = A5 sheet, 148- by 210-mm
.                    12 = B4 sheet, 250- by 354-mm
.                    13 = B5 sheet, 182- by 257-mm
.                    14 = Folio, 8 1/2- by 13-inch
.                    15 = Quarto, 215- by 275-mm
.                    16 = 10- by 14-inch
.                    17 = 11- by 17-inch
.                    18 = Note, 8 1/2- by 11-inch
.                    19 = #9 Envelope, 3 7/8- by 8 7/8-inch
.                    20 = #10 Envelope, 4 1/8- by 9 1/2-inch
.                    21 = #11 Envelope, 4 1/2- by 10 3/8-inch
.                    22 = #12 Envelope, 4 3/4- by 11-inch
.                    23 = #14 Envelope, 5- by 11 1/2-inch
.                    24 = C Sheet, 17- by 22-inch
.                    25 = D Sheet, 22- by 34-inch
.                    26 = E Sheet, 34- by 44-inch
.                    27 = DL Envelope, 110- by 220-mm
.                    28 = C5 Envelope, 162- by 229-mm
.                    29 = C3 Envelope, 324- by 458-mm
.                    30 = C4 Envelope, 229- by 324-mm
.                    31 = C6 Envelope, 114- by 162-mm
.                    32 = C65 Envelope, 114- by 229-mm
.                    33 = B4 Envelope, 250- by 353-mm
.                    34 = B5 Envelope, 176- by 250-mm
.                    35 = B6 Envelope, 176- by 125-mm
.                    36 = Italy Envelope, 110- by 230-mm
.                    37 = Monarch Envelope, 3 7/8- by 7 1/2-inch
.                    38 = 6 3/4 Envelope, 3 5/8- by 6 1/2-inch
.                    39 = US Standard Fanfold, 14 7/8- by 11-inch
.                    40 = German Standard Fanfold, 8 1/2- by 12-inch
.                    41 = German Legal Fanfold, 8 1/2 by 13-inch
.                    256= Custom user defined
.    140     10      Custom paper length in 10ths of mm
.    150     10      Custom paper width in 10ths of mm
.    160     10      Printed output scaling factor
.    170     10      Num of copies printed if multi-page copies supported
.    180     10      Paper feed source as follows:
.                    1 = Upper tray or only one
.                    2 = Lower tray
.                    3 = Middle tray
.                    4 = Manual
.                    5 = Envelope
.                    6 = Envelope Manual
.                    7 = Auto
.                    8 = Tractor feed
.                    9 = Small FMT
.                    10 = Large FMT
.                    11 = Large Capacity
.                    14 = Cassette
.                    15 = Form source
.    190     10      Print quality as follows:
.                    -1 = Draft
.                    -2 = Low
.                    -3 = Medium
.                    -4 = High
.                    nnn = Dots per inch when positive
.    200     10      Color usage as follows:
.                    1 = Monochrome
.                    2 = Color
.    210     10      Type of double sided print as follows:
.                    1 = Simplex
.                    2 = Vertical
.                    3 = Horizontal
.    220     10      Copies being collated as follows
.                    0 = No
.                    1 = Yes
.    230     8       Current vertical print position
.    238     8       Current horizontal print position
INCH_PER_MM      FORM    "0.03937"    ;conversion factor for MM to inches
.                         0.039370078740157 = full factor
PID_PG_I_WIDE    FORM  8.8  ;use for conversions
PID_PG_I_HIGH    FORM  8.8  ;use for conversions
PID_DRV_VER      DIM  8     Current Printer Driver version
PID_PG_P_WIDE    DIM  8     Page width in pixels
PID_PG_P_HIGH    DIM  8     Page height in pixels
PID_PG_M_WIDE    DIM  8     Page width in millimeters
PID_PG_M_HIGH    DIM  8     Page height in millimeters
PID_COLOR_BITS   DIM  8     Number of color bits per pixel
PID_NUM_OF_FONTS DIM  8     Number of device specific fonts
PID_PIC_OK       DIM  1     Can Print Pictures ? (Y or N)
PID_USER_COPIES  DIM  4     Copies specified by user (future)
PID_PRINT_ALL    DIM  1     Print All Pages (Y or N)
PID_PRINT_SEL    DIM  1     Print Selection Only (Y or N)
PID_START_PAGE   DIM  8       Print dialog start page value
PID_END_PAGE     DIM  8     Print dialog end page value
PID_ALIGN_RIGHT  DIM  1     Print Alignment Right (Y or N)
PID_ALIGN_DEC    DIM  1     Print Alignment Decimal (Y or )
PID_FILL_ON      DIM  1     Fill On Enabled (Y or N)
PID_PICT_RECT    DIM  1     Pict Rectangle Enabled (Y or N)
PID_OVERLAY      DIM  1     Overlay Enabled (Y or N)
PID_NAME_SIZE    DIM  4     Number of characters in Printer Name
PID_NAME         DIM  31     Printer name
PID_ORIENTATION  DIM  10     Print orientation CODE
PID_PAPER_SIZE   DIM  10     Paper size CODE
PID_PGLEN_MM     DIM  10     Custom paper length in 10ths of mm
PID_PGWIDTH_MM   DIM  10     Custom paper width in 10ths of mm
PID_SCALING      DIM  10     Printed output scaling factor
PID_COPIES       DIM  10     Copies printed if multi-page copies supported
PID_FEED_SOURCE  DIM  10     Paper feed source CODE
PID_QUALITY      DIM  10     Print quality CODE
PID_COLOR_USAGE  DIM  10     Color usage CODE
PID_DOUBLE_SIDE  DIM  10     Type of double sided print CODE
PID_COPYCOLLATE  DIM  10     Copies being collated CODE
PID_V_POS        DIM  8     Current vertical print position
PID_H_POS        DIM  8     Current horizontal print position

Printer Management Issues
There are a number of things to keep in mind with printers. I'll note some here as they come up.

GETINFO type=font for printers (use for justifying print lines)
This instruction allows you to get all the information that you need to justify print lines. It returns the width in pixels for any line when given the FONT and the PRINTER.

MMCC has a generalized loadmod called MMCCJUST which will do all of this for you. Give it the string, font and printer and it will return a table of lines to fit the width that you define. (In the UTIL-PLB folder.)

GETINFO TYPE={font object}, {string}, PRINTER={printer object} returns a predefined string defining information about the string as it would apply if tinted on the named printer object.

The fields for this info string are defined in the COMMONWK.PLS include unit.
.   You can get this with:          4/04/2001
.        UNPACK    STRING,     FONT_INFO
.        The GETINFO will read the contents of STRING based on
.        the FONT OBJECT and return, in STRING, the following
.        string of information:
FI_ASCENT     FORM   4   Pixels above baseline for a character
FI_DESCENT    FORM   4   Pixels below baseline for a character
FI_HEIGHT     FORM   4   Height of a character (ascent + descent)
FI_FULLHEIGHT FORM   4   Full height character (height + external L/S)
FI_AVG_CHAR   FORM   4   Average character width - generally letter "X"
FI_MAX_CHAR   FORM   4   Maximum character width - wides in font
FI_FONT_S_W   FORM   4   Font string width truncated to 4 bytes
FI_FIRST_CHAR DIM    1   First character in the font
FI_LAST_CHAR  DIM    1   Last character in the font
FI_PIXELS     FORM   8   Last character in the font


Here are some of the things we've learned when using PRTPAGE.

*Ph:v Relative positioning

For simple reports, use simple controls. A plain PRTPAGE will print the line and move down to the next line on the page just as you'd expect.

For accurate positioning, like on pre-printed forms, or forms where you'll be drawing boxes and lines, use RELATIVE positioning. That is, use the "*Ph:v" commands just as you would on a screen display

We typically do EVERYTHING with relative positioning.

We also prefer to use the *UNITS=*LOENGLISH setting to position thing in 1/100th's inch.
For example:
ONE          FORM    "1"     Just a constant "1"
PAGENUM      FORM     3      Page number
MAXLINES     FORM    "55"    Max lines we can get on a page
LINECNT      FORM   " 1"     Line we're at on the page
LH           FORM     5      Calculated HORIZONTAL position
LV           FORM     5      Calculated VERTICAL position
LINE_HEIGHT  FORM    "10"    Height in 1/100th inches
print_data   CALC     LV = LINECNT * LINE_HEIGHT
             CALC     LH = 10
                                   *PLH:LV,"Here is the data"
             CALL     LINECHEK
.        Central page overflow tester
.        Count the line just printed and see if room for more.
linechek     ADD      ONE,LINECNT
             IF (LINECNT < MAXLINES)
pagehead     ADD      ONE,PAGENUM
             MOVE     ONE,LINECNT
             PRTPAGE  PRINTER_FILE;*F:
                          "HEADING STUFF":
             ADD      FIVE,LINECNT


Use the "*UNITS=" to control how you'll address the positions on the page. We like "*UNITS=*LOENGLISH" which is 1/100th's inch.

NOTE: Despite what Microsoft might like you to think, you can't count on different printers positioning things the same. We've learned this the hard way using tightly positioned, pre-printed forms. On one printer / computer we hit the mark exactly. On the next printer / computer things are off. It's like each manufacturer uses a different ruler when marking out 1/100th inch.

As a result, we define EVERYTHING as relative positioning and we provide a setup dialog so that the end user can adjust the parameters if necessary. For example:
Top Margin 15
Line size 12
Middle column 250
Footer position 800

*PICT allows you to do great things with pictures. You can put them anywhere on the page.

You need three things for a PICTURE: the PICT definition, a CREATE, and the actual print statement:
PICT_NAME    DIM      30


             CREATE   PICTURE_THING=20:60:20:60,PICTURE_NAME


That's all folks. But here are a few things we've learned.

First, the runtime seems to preserve the aspect ratio regardless of the "top:bottom:left:right" that you use. Pick the dimension that you want to control and then make the opposite dimension smaller than it will ever be.

That is, if you want to control the TOP and BOTTOM but can let the left/right float, then set top and bottom accurately and make the left/right smaller numbers. The width will follow the height.

Pictures normally have BORDERS. There's a property for the PICT item called *BORDER. It works on the screen but NOT on the print page.

For the printer, you want to use the "*PICTRECT={*ON|*OFF}" control item within the PRTPAGE string:

Everyone complains that Windows does not respect the old PRTSCRN key on the keyboard. In truth, sometimes it does and sometimes it does not. There are various alternate key sequences:
  • PRTSCRN, Shift-PRTSCRNM and Ctrl-PRTSCRN Copy the entire contents of the screen including the taskbar, desktop, and everything else to the clipboard.

  • Alt-PRTSCRN copies only the active screen to the clipboard, regardless of whatever else is exposed.

PLBWIN provides a handy tool for getting the active window and putting it into a PICT object. That object can then be printed under program control!

The instruction is a very simple METHOD:
      {window-name}.FormToPict GIVING {pict object}
You must define the pict object in your program, but the method will take care of the CREATE for you. Once you have the picture, you print it just like any other picture with any of the controls, and sizing that you want.
          F1_Window.FormToPict GIVING FORM_PICTURE
          PRTPAGE  PRINTER_FILE;*P=10:01,"Here's the screen print:":

The PRTPAGE instruction includes a couple of color controls: *FGCOLOR and *BGCOLOR.
According to the PLBWIN manual:

*FGCOLOR={*color | value}

This list control sets the foreground color for those printers that may print in color. The {*color} operand may be one of the following: *BLACK, *BLUE, *CYAN, *GREEN, *MAGENTA, *RED, *WHITE, or *YELLOW. If {value} is specified it must be a numeric variable, equate or expression that evaluates to a twenty-four bit color number.

When using the number, you can do it as an integer with a hex value:

  PLB_Color Integer 4,"0x3333cc"


Note that the color may be messed up. We use an internet editor to get color codes. Where that program may show CC3333, PL/B required you to swap the first and last bytes as in 3333CC.

If the printer does not support color, the value is automatically converted to grayscale.


Send e-mail to MMCC.

Write to MMCC Technical Support at:
MMCC, Inc.
600 W. Midland
Bay City, MI 48708
(989) 686-8860


Write to MMCC Technical Support at:               Send e-mail to MMCC.
MMCC - Technical Support
600 W. Midland
Bay City, MI 48708
(989) 686-8860
© 1997 - 2017 MMCC - All Rights Reserved