# FilePro and Laserjet Printing

by

Jim Asman

Spectra Colour Svcs, Ltd.

Vancouver, BC

jim@spctra.wimsey.com

The filePro directories "ljet" and "widths" described in this article are on The filePro Bible CD.

They are in the directory ...\fpfiles, and must be copied to your system in order to be used.

Introduction

The modern laser printer can add much to filePro output but, as the "hplaser" print code table is ostensibly having the laser emulate a line printer, we really need a new table. Aside from deciding on a structure to make some logical sense out of the 256 codes in filePro 4.1, programmers must take a whole new approach as to what the printer can do for them.

This article is an attempt to build a useful print code table for both Hewlett-Packard LaserJet III and LaserJet 4 printers (while keeping an eye toward future developments). Because the LJ4 has many more internal fonts than the LJ3, and the LJ4 fixed pitch fonts are now scalable with different typeface numbers than those on the LJ3, I have worked up separate tables for each printer. This article is NOT intended to be a tutorial on the use of the LaserJet, as HP has material to do that very well, and the reader is well advised to get the PCL Technical Manual, if that hasn't been done already. You can use it quite effectively on a "need to know" basis, and get into it as deeply as you wish.

It is probably safe to assume that the majority of filePro applications running on a laser printer today are using either the Courier, lp.16, or another monospaced font. The printers, though, are capable of much more, and with a little diligence you can make your filePro reports and forms look more like they came from a word processor than a database. In the case of the LaserJet Series III and more recently the LaserJet 4, using the internal scalable fonts creates possibilities that we would never consider using a dot matrix printer, and further, the added graphics capabilities by themselves can change the "look" of the output entirely. If you want a fat line, a skinny line, a double ruled box, just name it and you can probably do it without very much effort. The HP-GL/2 plotter language can simplify drawing boxes and other graphic shapes, but that will have to be a topic for another time.

First Things First

Because of the sheer volume of useful printer codes (and maybe that should be rephrased to the codes that you do use), an orderly definition of the printer codes in the table needs to be established. In using a line printer, most of us probably never embrace more than a dozen or so printer codes in our day to day doings, but with the laser, the current limit of 256 codes on the table with filePro 4.1 is already restrictive, and that will only get worse as new printer features become available.

To this end, abandoning the 54 "universal" codes and starting over from scratch may be worthwhile just to maintain structure in the table itself, but recognizing that many people have built existing applications relying upon the supplied tables, I haven't changed the functionality of any of the codes at the beginning of the "hplaser" table, other than the printer init (code #3), which now resets the printer to the control panel settings, sets the PC-8 symbol set for both the primary and secondary fonts, and nothing else.

It makes sense to lay out the printer code table in categories, much like Hewlett-Packard has done with their reference materials. So let's go through it in some order.

Job Level

There are any number of printer codes that we may need to issue to the printer before the first character is ever printed and which are generally only sent once during the print run. I guess we have always thought of this as the printer initialization, and there are a lot of possibilities. We need to reset the printer first, define the page size, number of copies, margin sizes, page orientation, initial font, etc., etc.

All of this can be amalgamated into a single filePro code, but because the number of permutations becomes so large, I suggest that we use a generic printer init (code #3) and then get into the specifics with the form init. An implicit assumption made through all of this discussion is that we are not relying upon the lp interface script to establish any of the printing parameters, and obviously, if we have a printer reset code in our filePro printer init, the goings on of the interface script become academic. Certainly, any printer setup that you can use from the interface script is something that filePro doesn't have to deal with, but I see that script as something largely hidden and soon forgotten as to what it's doing.

Logically, then, we build our form init code by assembling other codes from the table. This need not be complex at all, and probably would only use a collection of a few codes put together most of the time. The printer reset code sets all the printing parameters to the values set at the printer control panel. Unless you are the only person using the printer, and maybe not even then, it is wise to assume that the values at the control panel may have been modified by yourself or others and should be set in your initialization of the job. Has anybody set the number of copies at the panel to some number other than one, and not reset it before the next job was printed? I know that I have. To be safe then, our form init should contain codes to set the number of copies, symbol set, font selection, paper source, page layout, and simplex/duplex printing, if you have a duplex printer.

Page Layout

Take a look at code 55. $1b &l2a0o6d3e60F

| | | | | |

$1b Sequence start---------------| | | | | |

2a Letter size paper--------------| | | | |

0o Portrait orientation-------------| | | |

6d 6 lpi------------------------------| | |

3e Top margin of 3 lines----------------| |

60F 60 Print lines--------------------------|

With this code we are telling the printer we have letter size paper, want to print 6 lines per inch, a top margin of 3 lines (1/2 inch), and a text length of 60 lines (10 inches). The PCL-5 language has no definition for the bottom margin as it is implied by the size of the top margin and text length. In this case, starting with an 11 inch sheet, we use 1/2 inch on top followed by 10 inches of text, thus we are left with 1/2 inch at the bottom. So, the bottom margin is really defined by the text length and top margin. Look at the other codes for 8 lpi in portrait and 6 lpi & 8 lpi in landscape mode, and study them until you understand the numbers. It's quite simple really.

Given the relative ease of formatting the page on the printer, I think that all filePro forms and reports should be defined as having the same number of lines to print as there are lines per page. The printer can do the page formatting quite nicely, and it saves filePro from having to send blank lines to fill the page.

Font Selection

Before discussing the selection of fonts, let's define just what a font is, to avoid confusion. Many of us, myself included, will use the words font and typeface interchangeably but, as the terms are used with a laser printer, they are not the same thing. In fact, the typeface is just one of several characteristics that describes a font. Quoting the HP Technical Manual...

"A font is a set of characters that have similar characteristics. A font has an assigned name, typeface, and is further described by its spacing, height, pitch, style, stroke weight, symbol set, and orientation."

While this may seem apparent, it is important to understand that if we want, for example, a Times 10 point medium upright proportional font, we would need a print code:

$1b (s1p10v0s0b4101T

| | | | | |

Sequence start--------| | | | | |

Proportional Spacing----| | | | |

10 pt----------------------| | | |

Upright(not italic)----------| | |

Medium stroke(not bold)--------| |

Typeface(4101 is Times)-------------|

Then later, to get the same thing in bold, we would only have to issue the "bold" code $1b (s3B). By giving the bold command, we haven't asked the printer to just modify the existing font, but are actually requesting a totally different font. So changing any of the font select parameters is changing the font and, if the printer doesn't have that font, you may get some surprises.

A font is selected by the printer based on its characteristics in the font select command, and the printer will do its best to oblige but, if there is no font that matches exactly, then there is a pecking order of the characteristics that will determine which font is picked. In order of highest priority, the determining characteristics are as follows:

Symbol Set

Spacing(proportional or fixed pitch)

Pitch(cpi--valid only for fixed pitch)

Height

Style

Stroke Weight

Typeface

Location

Orientation

This is all covered quite well in the technical manual, and it is worth the time to study it.

In our printer code table, codes 88-97 contain font selects for all of the LaserJet fixed pitch fonts, while codes 101-128 are font selects for 10 point proportional fonts for all the internal typefaces supported by the printer. Codes are provided for both the primary as well as the secondary font. Primary what?

The printer can maintain two distinct font select tables, with one of them being active at any given time. If you look at the font select codes in the table, you will see the codes for both primary and secondary. They differ only in the start of the code sequence. What you can do is have the two fonts designated and bounce back and forth between them by alternately sending codes 99 and 100.

These two tables needn't have anything in common, but they can. The secondary font could be the same as the primary, but italic instead, or could be completely different in every respect. Do whatever is most advantageous to you.

Symbol Sets

Although many of the LaserJet fonts contain well over 500 characters, we can only access a portion of them at any one time. The symbol set that is selected determines which characters are available and their order at any given time. Symbol sets are only a maximum of 255 characters in length, so you can see that if you only ever used one set, you'd be missing half the fun. The PC-8 set, though, has most of what you are likely to need in day to day applications.

As the symbol set has the highest priority in font selection, you want to make sure that the font you are selecting supports the symbol set that you've chosen, or quite likely you'll get something other that what you had expected. However, if you are using the LaserJet internal scalable fonts, you probably won't have to worry about that, as they support many different sets. On the other hand, the LaserJet internal bitmapped fonts (i.e., courier and lp.16 on the LJ3 and just the lp.16 on the LJ4), support far fewer, and if you were printing a bitmapped font and changed to a symbol set not supported by the font, you will end up with another typeface. This is all in the technical reference manual, but just to raise the level of paranoia ...

Using "vi" or your favorite editor, type in the following, which selects the lp 16 pitch font with the PC-8 symbol set and then changes midstream to the Desktop Symbol Set. For the purposes of this text, I will use "^[" to indicate the escape character, but keep in mind that you would use chr("27") in processing, or $1b when defining a printer code. To insert the escape character using "vi", type "CTRL-V" followed by the escape key. When entered, it will appear as the two character sequence "^[" on the screen, but it is just a single character in the file. Send the file to your printer and see what you get.

^[(10U^[(s0p16.67h8.5v0s0b0T

Carrots are divine

You get a dozen for a dime

^[(7JIt's maaaagic.

...B. Bunny

Food for thought, yes?

If you find you have an application where you regularly want to change back and forth from one symbol set to another, set it up with your primary and secondary fonts, leaving the other font attributes the same.

Just an aside here: in the print code table, codes 32-54, which are on the universal table, are various odd characters and others for drawing lines and boxes, some of which are not in the PC-8 set. If you look at them, you will see that the codes for a few, change symbol sets for the one character, and then change back to the PC-8 set. Of course, the assumption is that you are using PC-8 to begin with. If you are using any other set and call one of these codes, you could be in for some surprises after the code is finished; or in the case of those that are in the PC-8 set, you would almost certainly get a character other than the one you planned.

The Problem

Unfortunately, most of the goodness of the laser printer implies the use of proportional fonts and precise positioning of text on the page, but filePro output is designed for a fixed pitch font. I know that I didn't buy a laser printer to print courier fonts. filePro expects that any character position, let's say character 23 on the line, will always be in the same physical location along the line, regardless of what may have preceded. This is fundamental in output formats if fields are to line up in columns. That assumption certainly cannot be made when using proportional fonts. Even mixing 12 and 16 pitch monospaced fonts within a box has unwanted ramifications. So, in all likelihood, any existing output format that you now have which uses a fixed pitch would fall apart at the most fundamental level; i.e., to get the fields themselves to line up on the left, when using a proportional font. Our task is largely to align the fields and to take care of any alignment required within the field. Centering headings and the like also requires some attention.

Hewlett-Packard's PCL-5 printer control language provides us with all the tools we need to make use of the printer's many features, and it is, of course, the basis for the filePro print code table. At this point, let's have a look at why output formats can have problems once we start using the generally nicer looking proportional fonts.

Line Level

First, on a given line, we define any number of fields to be located at fixed positions on the line and, when a report is printed, these fields will all line up on the left, leaving us with columns on the page. Pretty standard stuff. We know that when this scheme is used with a proportional font, the columns won't line up, so rather than rely upon the amount of space previously consumed on the line to establish where a field will be printed, we will issue a code that positions the field at a specific location on the line, independent of what else may be printed on that line.

Field Level

There are three types of fields from our standpoint: left justified, aligned, and right justified. Left justified is our typical field where everything is pushed to the left: names, addresses, and the like. An aligned field usually would be a numeric field that is aligned on a decimal point or some other character: a dollar amount with a ".2" edit, perhaps. As the name implies, in a right justified field, all data is pushed to the right: integer numerics and perhaps text that for whatever reason you want to line up at the end of the word rather that the beginning.

All fields in filePro with fixed pitch fonts are really left justified, and any alignment that is required is taken care of by the "edit" that appropriately pads the field on the left or right side with spaces. It is, though, important that you conceptually understand the differences between them to make it work with the Laserjet.

Using Printer Codes

The LaserJet makes use of a wealth of PCL-5 printer codes to position the printer "cursor", select fonts, draw graphics, and on and on. The thing that makes it really interesting for a filePro programmer is that, aside from the escape character, chr("27"), that precedes every HP printer code, the rest is pure ASCII text. Actually, that is not quite true; the codes to activate the primary/secondary fonts are also non ASCII, chr("14") and chr("15"). This means that we can very easily put a printer code into a variable and integrate it right into our data, if appropriate, put it right on the output format as text, or in a code on the filePro printer table to achieve our goal.

Cursor Positioning

You can consider an 8 1/2 x 11 sheet of paper to be a 2550 x 3300 matrix of addressable dots, which translates to our 300 dpi but, as there are some margins imposed by the printer, our workspace isn't quite that large. We can direct the printer to start printing at any one of these locations, so you can see that cursor positioning is very precise.

Absolute Positioning

Before we go any further, let's get one term or acronym defined. CAP. This stands for "current active position", which simply means the place on the page that the next character will be printed, and is very analogous to the current location of the cursor on your monitor.

The code to position the cursor works like this.

^[*p200x600Y

This instructs the printer to move the CAP to a point 200 dots along the horizontal axis from the left margin and 600 dots down from the top margin. We can move only to the horizontal position by using ^[*p200X or only to the vertical position with ^[*p600Y. Note that only the last letter in the code is capitalized and that, when the command is combined, characters in the middle of the string are lower case. Further, these are absolute positioning commands, i.e., the position 200X means 200 dots from the left margin and 600Y means 600 dots from the top margin, regardless of where the CAP was at the time the command was issued. So ^[*p200x600Y is a unique position on the page. If you issue ^[*p200X, only the horizontal position is changed and the vertical location remains the same, and ^[*p600Y only affects vertical positioning.

Relative Positioning

We can modify the previous example to ^[*p+200x+600Y, which will direct the cursor to move relative to the CAP 200 dots further to the right on the X axis and 600 dots further down the Y axis, or ^[*p-200x+30Y would back up 200 dots to the left and move 30 dots down the page. The only difference is the inclusion of the "+,-" signs in the command. Relative positioning commands can be positive or negative, meaning that you can back up on the current line and overprint, if you wish to do so. It is all very flexible. I should point out here that, if you issue a positioning command that is beyond the bounds of the printable page, the new CAP will be at the printable margin.

Now how does this help us position our filePro fields? We only have to create filePro printer codes to create tab stops at regular intervals along the X axis. Ideally, we could have a predefined code at every 10th dot or so, but there simply isn't room on the print code table. To cover a 10 inch wide line with codes every 10 dots would by itself require 300 codes, so that is obviously out of the question at this time. The table presented here has a group of codes in 50 dot increments. While a tab at every 1/6th inch would seem adequate, you may find that positioning fields onto a preprinted form may require a finer resolution. When we define the output format, the appropriate printer code to position the field is buried under the first character of each field. For this purpose, we want absolute positioning on the X axis and, if you look at the codes beginning at #200 in the filePro print code numbering scheme, you will see there are enough codes to cover a 9+ inch wide line. As the positioning codes begin at 200, it is fairly easy to work out what code you need without consulting the table.

With the codes defined every 50 dots (which is 1/6 inch), when you need a code that will position your field 3.5 inches from the left margin, you will know that code 221 (6*3.5 +(200)) is the right one. We don't really need a code for a field that begins on the left margin, as the margin is automatically tabbed as it were, but you will find from time to time that you want to insert a couple of print codes before a field that is on the margin. With the positioning code, we can insert the spaces without changing the field's location on the printed page. That is really all that is required to line up the fields on the line. You may find that you want to change these values some to narrower tab stops, if necessary, to put the fields exactly where you want them on the line, but this is a place to start. If our print code table could be of unlimited length, then we could have a tab at every dot position!

Even though the fields are now lined up into columns, we've still got a real problem with with right justified text and numeric fields. Using a fixed pitch font, filePro simply left pads the field with spaces and then treats it like any other field. For positive numbers, this would work with proportional fonts if the width of the space were the same as a numeral but, unhappily, they are different on any of the LaserJet fonts I have seen. There are two general approaches to get numeric fields aligned.

If there are no characters other than a space or a numeral before the alignment point in the field (that means no commas or leading minus sign), then there is a relatively easy way. This means 10000000, 1000000-, 1.00, 1.00- are all OK, but 10,000,000 , -1000000, -1.00 are not. Given that restriction is acceptable, then we can treat it as a left justified field. There is still a problem, in that the width of the space is different from the numeral, but HP has conveniently provided us with something called the Horizontal Motion Index or HMI.

The HMI is the mechanism within the printer that defines the pitch of a fixed pitch font. You could be printing a 12 pitch font and, by changing the HMI value, have the same font print at 8, 10, 18, or some other pitch. I don't know why you'd want to do this, but you could. Now back to our story. When you are using a proportional font and issue a new HMI value, only the width of the space is affected. So all we need to do is make the width of the space equal to that of the numeral and then our numeric fields will print just fine. The HMI command goes like this: ^[&k###H (where ### is the desired width of the space in 1/120ths of an inch and is valid to four decimal places). If we know the width of the numeral, some simple math will give us the correct HMI value (numeral width in dots * .4). Only the Times and Univers fonts were considered when the codes were created. It is quite likely that many of these codes will coincide with some of the additional LJ4 fonts.

The numeral widths come from a width table for each font and point size which we will get to shortly, but in the meantime, you can look at printer codes 184-199 in the table that make reference to the HMI and indicate the font(s) and point sizes where the numeral and space character widths will be equated.

Be warned that after you have issued an HMI command to the printer, it will revert back to its default setting when you subsequently change any font characteristics, such as bold on/off, for example. To use the HMI command in your output (and this assumes that you have the HMI values in your filePro printer code table), you would put the appropriate HMI printer code under the character just before the numeric field, as the first character in the field already has a cursor positioning code there.

If you must have commas (etc.) in your numeric field or you want to right justify a text field, then you will have to have filePro consult a character width table to establish the printing width of the field and then issue an appropriate cursor positioning command. All of this will have to be calculated in processing, as the data contained in the field will directly determine the positioning values needed for proper alignment. Essentially, we read the widths of all the characters in the field up to the alignment point, total them, subtract that amount from the dot position on the line where we want the fields to align, and then have the field print beginning at that point. For example, if you want to align some text fields on the right at dot position 2100, you would first measure the length of the field, assume real field #6, which works out to be 622 dots, let's say, then create a dummy variable that contains ^[*p1478X{6, and that dummy field would be the field on the output format. Note that the dummy variable contains both the positioning code as well as the field's contents. For numeric fields with decimal values, you would establish your alignment point at the decimal, rather than the end of the field. It isn't as complex as it probably reads, as you will see in examples presented later.

Character Width Tables

The width table is but a list of the character widths for each font and point size. For the HP scalable fonts, we only need to have one table for each of the font attributes, i.e., upright, bold, italic, and bold italic, etc. The character widths scale in direct proportion to the point size, so we can derive the width for any point size from a single table.

Hewlett-Packard was kind enough to send me the font metric files for the LJ3 internal scalable fonts. From that data, I have extracted into a filePro database the character widths of all characters for all of the symbol sets that the LJ3 supports. This is a lot of info, and the key file is on the order of 250K and is really quite easy to use in your programming. Similarly, the font metric files for the LJ4 were uploaded to the HP forum on Compuserve when the printer was announced, and those have been put into a filePro database as well. With a significant increase in the number of internal fonts and supported symbol sets, the key file for the LJ4 is just under 2 meg.

If you want this information, you'll have to get the "cookBook" disk for this issue, as there is simply too much there to print, and I think it unlikely that anyone would have the inclination to key it in anyway. More on the width tables and symbol sets in a bit.

I should probably interject here for those of you that are thinking about getting a laser printer, that Hewlett-Packard, aside from producing a fine printer, is really a good company to do business with. Their customer support is absolutely outstanding. If the person on the phone doesn't know the answer, they will find out and call you back. Their technical manuals are very well written, and it is clear that a great deal of care is taken in their preparation. I would personally need a very good reason to go to another brand. The LaserJet 4, with its 600 dpi and increased font selection, looks pretty attractive right now. I recently bought one to have at home.

For non scalable fonts, you will have to have a separate table for each of the font attributes and point sizes that you want to use. If you have some other fonts for which you don't have the character widths, send me some email, and I'll try to tell you how to measure them yourself, if I don't have the information somewhere.

filePro Output Formats

It is useful when inserting PCL codes into your output format, whether the code is integrated into one of your filePro variables or in a traditional filePro printer code, to consider what is actually sent to the printer. Take a "form" that is defined as 75 characters wide and 60 lines long.

In theory, if you didn't put anything on the form, filePro would direct the printer to print 60 lines of 75 spaces, yielding a blank page. In fact, when filePro detects that there is nothing else to print on the line, it immediately sends a linefeed.

Now, if you put a heading or other text on the form, that text simply replaces the spaces that would have been there otherwise. Similarly, if you put a field on the form, e.g., *1, the spaces on the form are replaced by the contents of the field, beginning at the character position of the asterisk. It is important to understand that if you have a second field on the same line that is too close to the first field, i.e., you haven't allowed enough room for the first field, then the first field will get truncated and overwritten by the second field. Remember this. Its importance will become apparent later.

A filePro printer code, on the other hand, is inserted into the byte stream to the printer just before the character position it appears at on the line, in which case, we get our byte count effectively lengthened for that line, assuming, of course, that the printer code is non printing. A printer code that actually prints a character and is defined that way in printer maintenance will replace the "space" at the appropriate character position.

The point that I am making here is that any filePro output, be it a form or a report, is a long stream of bytes which arrives at the printer, and that by inserting various printer codes into this stream at the appropriate spot, we can get our output formatted as desired. Ultimately, it makes no difference if the print code got into the stream, as filePro code, as a variable, or directly on the output format itself. Things will be much tidier on your output format itself, however, if you can make use of a filePro printer code, but this isn't always possible.

An Example

Before getting into the actual implementation of these concepts in filePro, we will create a raw text file using "vi" or your favorite editor and print it out as a demonstration. Enter the text in Figure 1 into a new file. The grid showing the character numbers on the line shouldn't be entered, as it is only there for clarity.

The first section of the text prints data that is typical of (but abbreviated from) what many of us print in our day to day accounting. The very first line is the printer setup and selection of the courier 12 pitch font. The fields begin at the margin for the name, character position 31 for the invoice number, and at position 43 for the amount. Note that the amount field is left padded with spaces to fill a (8,.2) filePro field. This prints as we would expect. Refer to Figure 2.

Now, the second section begins with a font change to the Times 12 point proportional font. You can see that our output at this point is far from satisfactory. Let's get the fields in line first. The left margin is OK but the other two fields need some work.

The invoice number begins at character position 31, which at 12 pitch translates at 300dpi to dot position 750, so, if we insert the printer code ^[*p750X immediately before the field value, those fields will be aligned. If you don't understand where the 750 came from, we'll work it through. We want to know at which dot position the 31st character begins. At 12 pitch each character is 25 dots wide(300/12). The first 30 characters then consumed 750 dots(30*25), but as the PCL coordinate system begins at position 0, our 750 dots are filling positions 0-749, conveniently starting char position 31 at dot 750. The same is true for the amount field at character 43, which translates to dot position 1050. The third section now has the fields aligned on the left, although the amount field doesn't look like it. The problem there is, of course, that the space and numeral characters have different widths.

By inserting the HMI printer code ^[&k10.0H just before the beginning of the section, we have forced the width of the space to be the same 25 dot width as the numeral. The fourth section then looks like what we are after. It is purely coincidental that the Times 12 pt numeral happens to be the same 25 dots wide as a 12 pitch monospaced font character. There is one thing, though, which probably isn't apparent in this example, and that is that, when you have the space widened to the numeral width, it can appear visually too wide in normal text; and, as mentioned earlier, if you change a font attribute, the HMI value reverts back to its default value. So what we can do is, instead of issuing the HMI code right at the beginning of the text, we will insert it just before each numeric field's positioning code, and then just after each of the field's contents, issue an effectively null font attribute code. In this case, it was ^[(s0B, which has no effect, as we are already printing non bold. As we are already printing at 12 pt, the command ^[(s12V, which would select 12 pt, would have achieved the same thing, namely, to restore normal spacing without changing any other of the current printing characteristics.

The fifth section should then represent our original text, now aligned perfectly, just like the Courier 12 pitch, but now using the Times proportional font.

I strongly recommend that you enter the text into your own computer and get it to behave as shown. At that point, experiment a bit with changing the dot positioning values to move the fields around. Put some extra spaces just before the dot positioning command, and you will see that it has no effect on where the field prints, as we are giving the printer absolute positions on the line to place the text. Try reversing the 750 and 1050 values, and you will see that the fields print transposed from their locations in the text. I can't think of a reason that you might want to do this, but it's possible.

Actually, if you had two or three numeric fields on one line that were separated by some text fields, you could place them side by side on the form with a beginning HMI code and a trailing null font select code, and then, if your positioning commands were correct, it would all be located properly on your output, but would have only required one HMI command. It would certainly make the output format confusing to the uninitiated, but could make the format design a bit easier.

Enter some HMI values that are wrong to see what happens. If you fully understand this example using raw text and inserting the printer codes manually, as it were, you will find all the rest of it will come very easy.

filePro Formats

Referring back to the previous example, let's put the detail lines into a filePro report. To simplify the discussion, we will ignore any header or subtotal sections and just look at the detail lines. We will define the fields as follows.

Field No. Description Length/Edit

1 Name (24,uplow)

2 Invoice No. (5,*)

3 Amount (8,.2)

Now refer to Figure 3 to see how this is laid out on the report format relating the filePro printer code numbers to the supplied table.

All that we have done in defining the report format is to insert the appropriate printer code from the table into the format at the right place, and that is really all there is to it. Presumably, the output from our report format will be essentially identical to what we previously did by hand. Note that the positioning codes are buried under the first character of their respective fields, and this is an absolute requirement. If there are any spaces or anything else between the printer code and the field's beginning, then our alignment goes right in the toilet. In the case of the HMI code, it is only required that it arrive at the printer sometime before the field is to be printed and sometime after a previous field that may be adversely affected by the code is encountered in the format.

Undoubtedly, we will never have enough room on the printer code table to meet every requirement, and further, some of the values required for the code may have to be established at runtime; so what we can do is to put the printer code into a variable and put the variable on the output format or, more than likely, integrate our filePro data into one variable that contains both the printer codes as well as the data. This is all done in processing.

There are two different approaches we can take in putting the printer codes into filePro variables. First, we can maintain our concept of fields on the report, and simply replace real fields on the format with dummy variables that contain both the printer codes and the data in the real fields; or we could have one long variable that contains everything we need to print on a single line. The former is probably less complex in processing, and the latter makes the output format definition an absolute breeze. The advantage, if you can call it that, of the printer codes being contained within a dummy variable is that you don't have to rely upon any predefined values from your printer code table. All things being equal, though, your life will be quite a bit simpler if you can make do with the printer codes off of the table.

Once we start embedding printer codes into variables, we totally lose any concept of the variable's length as it relates to the length of the field as it is actually printed on the output; and, as mentioned previously, filePro will truncate any field that is overwritten subsequently by a field to the right of it on the line. If you have done your duty and slogged through the original exercise, you will understand that the physical location of something on the output format only need have a general relationship as to where it will appear on the printed page, namely on the same line, and not even there necessarily. Because, at the point of designing our format, we don't really know how wide our lines may prove to be including both data and printer codes, define the format as being arbitrarily wide, maybe 200 char or to the maximum of 255 char. There isn't, at least from observation, any performance penalty to speak of from defining the format from being overly wide.

Let's go back to our original report with the three fields. This time we will keep the semblance of the fields on the output format. The first field needs no special treatment of any kind as it is on the margin and is a left justified field. We just print it as filePro delivers it. Field 2, however, needs to be positioned on the line at dot position 750. Rather than put field 2 on the format, in processing, we will create a variable that contains both the positioning code as well as the field data. A processing line similar to the following will do the deed.

aa(12,*)=chr("27"){"*p750X"{2

Similarly, field 3, the amount, could be defined:

ab(29,*)=chr("27"){"&k10.0H"{chr("27"){"*p1050X"&3{chr("27"){"(s0B"

Note the use of the "&" join character in the variable "ab". Because the width of the space is being manipulated to achieve alignment, the use of a "{" join would squeeze out the spaces and destroy the alignment.

So then our output format could have:

*1 at position 1 on the format, *aa at position 27 on the format, and *ab at position 40 on the format. So, although the variable *ab eventually only prints 8 characters on the output, its length (including printer codes contained within) is 29, and if you wanted to print another field to the right of *ab, it had better be located 31 characters or more further to the right. The key thing to remember in this is that you have to leave enough room on your format for the entire variable, even though many of the bytes will be consumed by the printer and not appear on the page, and, of course, the format must be wide enough to accommodate the length of the final variable on the line.

Taking this a step further, we could define yet another variable:

ac(66,*)=1{aa{ab

In this case, the output format would only need to have *ac appear at position 1 of the line. This makes your output format rather tidy.

Finally, there is yet one other method that could be used to get our printer codes inserted into the output, and that is simply to put the code physically onto the format itself. We still need a printer code for the escape character (code 98), but as the balance of the HP codes are ASCII, we can put them directly on the format itself, leaving enough room for the fields, of course. See Figure 4.

Putting codes directly onto the format is quite often the most practical approach. First, it makes them immediately visible when reviewing a format on the screen, and it saves the need from defining obscure codes, particularly specific cursor positioning codes that in all likelihood would only be used on that particular format. Many times, you will find that you have to position the cursor at a specific dot on the line, perhaps to right justify some text, and then a "custom" printer code on the form is in order. You will see examples of this later.

Really, it should be a matter of personal preference as to how you get the codes into the output, and I don't see any particular superiority to any method. The nature of the output may very well determine the appropriate approach.

Graphics

We will limit the definition of graphics in this discussion to drawing and filling boxes. While it sounds somewhat simplistic, much can be done that you would never have considered with filePro before. Refer to Figure 5 as you read through this part of the text.

Again, think of our printed page as being a matrix of dots or pixels, if you prefer, and we "turn on" the appropriate dots to print black and the collection of dots that are "turned on" represents our printed output. When we place a character on the format, the printer turns on the dots that will create the character at that location on the format.

Similarly, the box drawing printer codes turn on the appropriate dots to draw the box, as per the specifications in the code. We have codes to draw a solid black box, a solid white box, and boxes filled with various patterns. A good question immediately arises. Why would you want to draw a solid white box on an already white page? Well, we draw white boxes to "turn off" or reset dots that have previously turned on. You see, if we draw a solid black box and then decide that we would like the center of the box filled with a halftone pattern, let's say, sending the code to print the halftone pattern would turn on its dots; but because we are printing onto an area that already has all its dots turned on, there would be no effect. Printing a pattern only turns dots on and it doesn't reset any. So if you want a halftone or some other pattern, you would, in this case, have to reset all the dots in the particular area before you laid down the halftone pattern.

The statements in the last paragraph are not always true. There are a couple of options in using fill patterns. The default is that the pattern is applied to the destination transparently, which means that only the black dots in the pattern are turned on and the white dots in the pattern have no affect on the destination. A good analogy to this would be the use of one of the yellow felt pens often used to highlight text in a book. Although the yellow covers the printing, you can still see the text through it and read the text, but if you covered the same text with an opaque yellow oil paint, it would cover the printing so it was totally hidden. If we send the code ^[*v1O to the printer, and then our fill pattern, both the black dots and white dots are copied from the pattern to the destination, i.e., any existing black dots on the page that coincide with white dots in the pattern will be turned off, effectively overwriting anything that was "underneath the pattern". Figure 5-G shows a box drawn this way. The situation will dictate which is the appropriate approach. It is probably wise to return the pattern transparency back to 0 when you are done (^[*v0O). Box drawing works like this. The following code will create a solid black box that is 1500 dots wide and 300 dots high.

^[*c1500a300b0P

The value that precedes the "a" is the width, and the value preceding the "b" is the depth. The 0 preceding the "P" is calling for a black fill pattern, but note that the 0 need not be present in this case, as the printer will presume a black fill in its absence. The box is drawn down and to the right of the CAP, but the CAP is NOT changed by the box drawing.

In most cases, a solid black box isn't terribly useful, but a really skinny long black box is. We call it a line.

^[*c1500a3bP

or try

^[*c3a1500bP

You can see then that we can draw lines any width and any thickness in increments of 1/300 of a inch. In Figure 5-B, you can see that by using cursor positioning commands we can draw a "double" line. Varying line thickness and using a double rule here and there is ideal for separating sub total sections and the like in reports. A General Ledger is a natural for this kind of treatment.

Back to the original example. Let's extend the code some.

^[*c1500a300b0P^[*p+5x+5Y^[*c1490a290b1P

Now what we have done is drawn our original box, then moved the cursor in 5 dots on both axes and drawn a white box that is 10 dots smaller. The "1P" part of the code represents a white fill or reset mode. We are left with a ruled box 1500 x 300 dots with 5 dot thick lines. If you want a fatter rule, move in a little further and draw an appropriately smaller white box. There are other fill patterns besides black and white. "2P" represents a shaded fill and "3P" represents a cross-hatch fill. Because both of these fill patterns have their own variations, we end up with another parameter in the code, e.g., to specify the level of the halftone shading. I will only deal with halftone shading and leave it to the reader to deal with cross hatching. The game is the same, the numbers are different, and it is all covered quite well in the tech ref manual.

Although the box drawing codes do not change the CAP, in the last example, we actually moved the cursor to draw the white box, and the CAP would now be at the upper left hand corner of the white box. As we go further, I will represent the last example with an ellipsis, to keep from getting bogged down repeating the same code.

Continuing now,

...^[*c1490a290b15g2P

The last addition to our code fills our "white" box with a 15% shading pattern. You can see that we now have a "2P" fill pattern and have picked up a new section in the code "15g", which is the degree of shading. There are actually only eight levels of shading available, and they are defined by ranges in shading percentages, and whatever range your value falls into will determine the actual density of the shading you get. At "15g" here, we are right in the middle of the "11-20" percent range. So, if we had used "11g", "15g", or "20g", the result would have been the same. If you want a darker or lighter shading, change the value that precedes "g" in the code. Again, get the tech ref manual.

If you want to create a box that has a drop shadow, draw a second box that is shaded unruled, offset by 50 dots or so from the "real box". It's easy. See Figure 5-F.

Boxes don't have to start out as black and be worked over. If you want a shaded unruled box, the new part of our code will do that by itself, as you will see in a more real application later. None of this is restrictive at all. One thing: when you draw a box using the PCL codes, that box is being deposited on the page at an absolute position, specific dots are turned on, and none of your text is going to change its location. Boxes drawn by filePro in graphics mode, on the other hand, are merely text characters that, when aligned, will create a box, but die swiftly if you are printing proportional text or if you change pitch within the box.

If you find that you are getting into some minor difficulties using the filePro generated boxes, you may find a simple fix in putting a cursor positioning code under box end characters. If the box were defined at 12 pitch and the right hand edge of the box was at character position 50, just put ^[*p1225X under the right edge vertical box characters and they will always print at the correct spot on the line.

One other thing that should be brought up is a further definition of the CAP as it relates to boxes and text. The CAP, when printing text, is located at the bottom left of a capital "M". This may not be 100% accurate, but is close enough for our purposes. It is on the baseline, i.e., the line that represents the bottom of capital letters and most lower case letters. From any CAP, a letter that is subsequently printed will ascend above the CAP, and many characters with descenders (g, p, q, etc.) will go below, so to put text into a box, you will either have to make some effort in the positioning of the box or the text itself. Once you play with it a bit, you should be able to establish your own guidelines.

If you start mucking with the CAP to position a box vertically on the page, you want to at some point return the cursor to its original position, in the interest of getting the CAP back in sync with filePro's idea of where lines are positioned; e.g., if, for some reason, you have issued ^[*p-25Y and don't subsequently do a ^[*p+25Y, then filePro lines that follow are going to be (at 6 lpi), a half line off. It could be a big deal or maybe nothing, it depends.

In the course of drawing some graphics, if you have cause to reset some areas back to white, then the order in which things get executed becomes important. If you draw a ruled box like the one in our earlier example and want to put text inside it, you absolutely must draw the box BEFORE you print the text, otherwise the text will get erased during the box's creation. Just the other night, I must have spent 15 minutes trying to figure out what was wrong in a processing table because of missing bits on my output. The processing was fine, my data was being erased by the graphics.

One extension of the box filling business is to use the different fill and shade patterns to print text in something other than solid black. Look at Figure 6 and refer to the "Print Model" section of your tech manual. The codes that produced the graphics are underneath each of them. A pattern filled character is most effective at larger point sizes, as the pattern doesn't scale with the text, and some patterns read better against a black background. In Figure 6, the code at the top right positions the cursor and draws the black box, and is prepended to the codes that have the box. Note that in the examples neither the pattern transparency nor the selected pattern are returned to their default values after "filePro" is printed, and in your programming you should make sure this is done.

Figure 7 shows the effect of first printing black text to create a shadow and then moving the cursor some and overprinting with an opaque pattern. The creation of the shadow is a good demonstration of the push/pop printer codes. To do this, we issue the code to create the shadow pattern, if it isn't the current pattern, and, in this case, our shadow is black. But before "filePro" is printed, we save the address where the word starts with "^[&f0S". After "filePro" is printed, we restore the address with "^[&f1S", which puts the cursor right back to where it was before the "f" was printed. At that point, we issue a cursor move command, in this case, ^[*p-10x-10Y, select an opaque pattern and overprint the black with the pattern. There are all sorts of themes and variations, a few of which are shown here. Note that in the third one we only have shaded the word "file". Let your creative juices flow! Obviously, any of these code sequences could be assigned into a filePro variable and put anywhere on your output.

This type of thing tends to be somewhat of a gimmick and trite, but there are situations where it is quite appro

priate. Don't use shadowed lettering because your printer can do it, use it because it adds something visually to your layout. You see it all the time on TV. When a new techno process evolves, every beer ad, car ad, and aspirin ad jumps on the bandwagon. The current one is the "morph".

I generally find it most efficient to develop a graphic sequence with a text editor, and once the codes are worked out, then worry about how to get it into the filePro application.

Practical Applications

I have created a filePro database called "ljet", which is a bare bones order entry file, purely for demonstration purposes. Within "ljet", there are a couple of output forms that implement many of the LaserJet features. You will see that the output formats are using "hp3" as the printer, and if you install your printer as "hp4", you will want to change this in the format "options".

First, is a form named "spine", and it could reside in any file, as the form supplies its own data. This is a small routine that prints a label for a binder which provides a clear sleeve down the length of its spine for titles. This is quite handy for labelling manuals for public domain software. You could modify this to make labels for your video tapes or signs for your store, etc. Get some Avery full page labels, stick one down on a piece of card, and you're done. We are going to draw some box shapes and center text supplied by the user at runtime in both normal and reverse type. As we are centering text that is supplied when the form is printed, the character width table supplies the text width information we need to center the text on the fly.

Have a look at both the processing table and output format for "spine." The processing is reasonably well commented as to what's going on. You will see that all of the work is completed in the processing table and the format has but four variables. There are no filePro print codes on the spine format. You could repeat any one of these labels by placing it on the form more than once. You do have to pay attention, though, that you don't put them so close together that they overlap.

The size of the labels reproduced here was construed to fit on the "cookBook" page and is really a bit small for a regular binder, so there is a second format on the disk called "spine1" that creates labels of a more appropriate length for use in a binder.

It is pretty straightforward. The first 9 lines establish our working variables and get the character widths into an array, and lines 10 through 16 create the label, which is moved into the variable "nl" for printing. Lines 17-24 print a similar label, but in reverse type. The remaining two labels are the same as the first two, except they are a bit deeper. Note at line 5, there are variables created for setting normal and reverse (white on black) text. Make sure when you go to white on black mode that you return to "normal" printing when finished or you may find yourself in possession of some blank sheets later on. In the processing here, the variable "bt" is tacked on the end of the print string in Line 24.

Calling the Width Table

The only thing that really needs to be brought up here is the use of the character width table. If you have the disk from this issue of the "cookBook", you will find a filePro file called "widths". Each record contains 265 fields, the first 255 being 1 point character widths in ASCII order. To use the table, you must first "lookup" the correct record for the font and symbol set in use and move the lookup record into an array. (See lines 6-7.) Fields 256-259 also contain 1 point font information in the same format as fields 1-255, so define your array with 259 elements. Field 256 has the default space width, 257 the recommended line spacing in dots, 258 the Caps Height, and 259 the "lowercase" height. Look at the "widths" map for a description of the balance of the fields.

Index A is built on field 261, which may require a bit of explanation. The index field is 8 char wide with the format, "ty ty wt st sy sy sy sy", where "ty" specifies the typeface, "wt" the stroke weight, "st" the style, and "sy" the symbol set. See the accompanying table, which gives mnemonic definitions for the typeface, stroke weight, and style. The final four characters are simply the call letters of the symbol set you are using. Some symbol sets only have a two character call sequence, in which case your search key would only be six characters in length, while others have three, and at least one on the LJ4 is four characters. In line 2, our search key (sk) is defined as "unbu10U". This lookup will find the width table for "Univers bold upright typeface PC-8 symbols".

Width Table Mnemonics

Typeface Mnemonic Typeface Mnemonic

Albertus al Omega om

Antique Olive ao Symbol sy

Arial ar Times ti

Clarendon cl Times New Roman tn

Coronet co Univers un

Garamond ga Wingdings wi

Marigold ma

Stroke Weight Mnemonic Style Mnemonic

Medium m Upright u

Bold b Italic i

Extra Bold x Condensed c

Condensed Italic k

Look at line 9 in the processing table. You will notice that our input string is assigned to the variable "l"(ell) which was never declared. If you squeeze out the spaces from both ends of an undeclared variable, filePro's length for that variable won't include any trailing or beginning spaces, and that is quite handy if we want to center text.

Centering Text Horizontally

We center text as we would normally using a fixed pitch font, i.e., to divide the whitespace evenly on either side of the text, but in this case we are working in dots, so the the process is quite precise. As mentioned before, when a box drawing operation is completed, the cursor remains at the upper left hand corner of the box.

The text length is measured in the subroutine "getwid" starting at line 41 in the processing table. We simply step through each character in the string using a "mid" statement and get the ASCII value of each character, which is stored in variable "cn". As our width array is in ASCII order, the width for that particular character is the "cn"th element in the array. Well, not quite.

Remember, the widths in the array are for a 1 point font with 5 decimal places, and we need to multiply the newly found width by the current point size to get the character width in dots. The character width is an integer value. Our actual character width is stored in the variable "cw", which is declared as an integer, and filePro handles the rounding perfectly. FYI, the printer rounds up on a fractional part of a dot .5 or greater, otherwise the fraction is ignored.

If you look at line 45, you will see that the widths for each character are accumulated into the variable "aw". There is a test at line 40 to see if we have reached the end of the text and, if that is the case, we drop down to line 48 to work out the necessary cursor move to center the text. Note at line 44, there is a test for chr("0"). The printer makes no cursor move upon receipt of chr("0") but, as we don't have an element wid("0") in the array, we must insure that we don't go looking for one. I don't know how a chr("0") would get into filePro output, but ...

Speaking of illegal characters, should you try to print a character not supported by the symbol set in use, i.e., the symbol set has no character for the particular ASCII value, the printer will print a space. The width table will get the width right but, of course, you won't get what you wanted.

In line 48, we subtract our text width from our box width and divide the result by 2 "xa=(bw-aw)/2", thus "xa" now contains the distance we need to move the cursor to center the text along the X axis. Although this move will center us left and right, the cursor is still sitting at the top of the box vertically.

Centering Text Vertically

The vertical centering of the text is a bit trickier in a narrow box such as this, not so much in the implementation, but more in deciding what to center on! Character heights vary, e.g., Aag, so you have to decide whether you want to center on the Caps height, lowercase height, or Caps height plus descenders, lowercase plus descenders, etc., etc. In practice, you really only have to decide between Caps and lowercase, with lowercase being the most common choice. It's purely a visual thing.

The more white space you have to play with, the less of an issue it becomes. This time, we will center on the Caps height. In the width table, field 258 contains the 1 pt Cap height and field 259 contains the 1 pt "x" height (lowercase). If you multiply these fields by the current point size, you will get the uppercase or lowercase character height in dots. Line 41 on the table puts the Caps height into the variable "ch". To get our vertical cursor move, we first have to get our text into the box. Presently, the bottom of our text is at the very top of the box. If we move the text down by the amount of the Caps height, all the whitespace would be below the text. To center it vertically, then, we want to move down further half the amount of the whitespace, which, by formula, would be (bh-ch)/2, and, at line 41, you see ya=ch+((bh-ch)/"2"). "ya" contains the cursor move we need to center the text vertically.

Finally, in line 48, the variable "h" contains the complete printer code to center our title in the box, both horizontally and vertically. The variable "h" is integrated into our print string immediately before the text and we are done.

Backing up just a bit, I would like to demonstrate how you would use the width table to align columns on a decimal point. Assume that the appropriate width table is already in the array "wid", the point size is in variable "ps", and that we would like to align real field 11 at dot 1819 on a report.

Then: pt="" { 11 { ""; ap="1819"; gosub getwid

...

...

getwid

Then: cn=""; aw="0"; cw="0"; pn="0"

loop

Then: pn=pn + "1"

if: mid(pt,pn,"1")="."

Then: goto dowid

if: mid(pt,pn,"1")=chr("0")

Then: goto loop

Then: cn=asc(mid(pt,pn,"1")); cw=wid(cn)*ps; aw=aw+cw

Then: goto loop

dowid

Then: pp=ap-aw; cp=chr("27"){"*p"{pp{"X"{11{""; return

The variable "cp" would appear on the output format rather than field 11. As we are aligning on the decimal point itself, it doesn't matter what precedes of follows the decimal. The field will always be positioned correctly. You could put in a further test to check for the end of the string, just in case something slipped in without a decimal point. You could use this routine to align numeric fields or even text in a format like "filename.ext". If, for some reason, you had the HMI set to something other that the default, you would want to return it to the default before printing if there were any spaces embedded in the text, as the width table assumes the default HMI.

A More Ambitious Example

Next, we will look at creating an "invoice" form that is somewhat more complex than the label exercise. The theory isn't any more complex, but there is more of it. First, I would say that, if you generate a lot of invoices in the course of business, it makes more sense to have your form preprinted, rather than using up your printer's CPU cycles and toner to continually print the same thing. On the other hand, some companies may only generate a dozen or two invoices a month, and many people might find a form such as this very useful as a purchase order or some other form that isn't counted in the hundreds each month. See Figure 9 for the printed output and Figure 10 for the output format layout.

While the output format doesn't appear terribly different from what we usually produce, there are well over 100 different printer codes hidden in the format, as well as a few that appear on the format itself. I tried for a number of days to come up with a clever way to show the codes and their location on the format, and the best that I could do was a list of line numbers with the character position and its associated printer code. See Figure 11.

With that said, there is still much to talk about. The very first line of the format has a number of codes to set up the primary and secondary fonts and their point sizes, etc. Lines 1-3 are blank, just for positioning on the form. The variable "hf" at line 4 is built in the processing table in lines 2-7 and contains the code to print the logo, company name, and the short black rule. Variable "hi" at lines 8-10 in processing set up the street address and phone number, and "hj" prints the long black rule in the heading. It's all pretty straightforward, if you've been paying attention so far. The trickiest part of the heading is positioning the cursor for the three independent variables that appear on different lines on the form. I should point out here that I kept all of the component parts of these things to less than a screen width in define processing so the table would print nicely here, but in a real application, you would find it easier to utilize all the space that filePro allows on a processing line. Note that, when "hf" is assembled at line 7, different join characters ("{&<") are used. This has to do with spaces that may be trailing one of the variables, and note that it's not coincidental that the address and telephone number text exactly filled the line. More on that in a moment.

10 20 30 40 50 60 70 80

....:....|....:....|....:....|....:....|....:....|....:....|....:....|....:....|

1

2

3

4 *hf

5

6 *hi

7 *hj

8

9

10 Invoice *p1373XInvoice No.: *5 &l8D

11

12 *p1540XDate: *@dt

13

14 Sold To: &l6D *p1270XCustomer P. O.: *6

15

16 *1

17 *2

18 *3

19 *4 Attn: Accounts Payable

20

21

22 *bx

23 *p351x-34YQty*p916XDescription*p1610XUnit Price*p1887XExt. Price*p+34Y

24 *11 *21 *31 *41

25 *ia

26 *12 *22 *32 *42

27

28 *13 *23 *33 *43

29 *ia

30 *14 *24 *34 *44

31

32 *15 *25 *35 *45

33 *ia

34 *16 *26 *36 *46

35

36 *17 *27 *37 *47

37 *ia

38 *18 *28 *38 *48

39

40 *19 *29 *39 *49

41 *ia

42 *20 *30 *40 *50

43

44 *bg

45 *p1383x-50YSub Total *p1823X*51

46 *ib

47 Terms: Net 30 Days *p1381XSales Tax *52

48

49 *p1302XInvoice Total *p1823X*53

50 *ib

51

52 *p+50Y

53

54

55

56

57

58

59

60

...............................E.N.D...O.F...F.O.R.M...........................

Figure 10

Line: Pos-Code, Pos-Code, ...

1) 1-100, 2-101, 3-116, 4-156

4) 1-206

6) 1-147, 2-171

10) 1-154, 2-173, 3-206, 10-134, 11-151, 43-98, 63-99,

70-98

12) 43-98, 50-100, 56-99

14) 1-100, 2-206, 11-98, 43-98, 66-99

16) 1-208

17) 1-208

18) 1-208

19) 1-208, 15-216

20) 1-136, 2-146, 3-171

22) 1-206

23) 1-98, 11-100, 14-98, 31-98, 48-98, 65-98, 71-151,

72-99, 73-195

24) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

26) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

28) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

30) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

32) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

34) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

36) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

38) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

40) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

42) 1-207, 6-167, 7-210, 58-195, 59-232, 68-237

45) 1-167, 4-98, 15-100, 25-99, 58-195, 61-98

47) 1-167, 2-173, 3-100, 4-206, 10-171, 12-99, 33-98,

40-100, 50-99, 67-195, 68-237

49) 1-167, 4-98, 11-100, 25-99, 58-195, 61-98

52) 1-98

Figure 11

The next thing of interest on the format begins at line 10, where you find "Invoice No.:", and similar entries on lines 12 and 14. These three lines have text that we want to align vertically on the right. Similarly, down at lines 45, 47, and 49, we want "Sub Total", "Sales Tax", and "Invoice Total" to align on the right. Our friend the width table comes to the rescue. Back to line 10. I decided that "Invoice No.:", "Date:", and "Customer P.O.:" should be aligned at dot 1675 on the line. So, to achieve this, we have to establish the exact width of the text in dots, and position the cursor at "1675-textwidth" before we print the text. The grunt is really to get the text width and the rest is easy. In the input processing for the "widths" filePro file, there is an "@keyw" routine that will prompt you for the "fontspec", the point size, and the text you want measured. Once you enter the text, you are given its width in dots. The processing does check for a valid point size, i. e., a positive number in .25 increments. The left-arrow key is used to "back up" to the previous input prompt, or to eventually exit the routine. For convenience, the code is also duplicated in the "ljet" input processing table.

In the case of the address/telno line in the heading, I had decided on the text and measured for an 8 pt width. I knew I wanted to fill 1569 dots, and at 8pt, I was a 150 dots or so short. At 9 pt, the resulting width was within a dot or two. Bingo!! If, for some reason, 8 pt were required, then the space between the address and phone number section of the line would have to be widened or the text lengthened a tiny bit. Remember, the fonts are capable of being scaled in .25 point increments, and that can be useful if you have to shoehorn some text exactly into a given area. There is ALWAYS a way to cheat.

"Invoice No:" etc. are printed in Univers 13pt bold and work out to be 302 dots, 135 dots, and 405 dots respectively. Then, by subtracting those numbers from 1675, we know where to position the cursor to get each of the "fields" to line up at dot 1675. You can do the math to reconcile the codes on the format. The "Sub Total" group are 242, 244, and 323 dots respectively and are planned to align at dot 1625. You could calculate all of this in processing at runtime, but, as these are static things on the form, it is best to do the math only once, beforehand.

Also, at the end of line 10, there is a printer code that changes the line spacing to 8 lpi. This is just to close up the next few "double spaced" lines. I went to double spaced 8 lpi as the 13 pt text is too tall for single spaced 6 lpi. I bring this up to point out that the LaserJet does NOT count lines when deciding it's time for a new page. When you do the page setup so it works out to 60 lines at 6 lpi, the printer establishes the page boundaries in dots, and a subsequent change in the lpi does not change these boundaries. Sixty lines at 6 lpi translates to 3000 dots, and the printer will only do a page eject when a linefeed or half linefeed moves the cursor past dot 2999 (our 3000 dots are numbered 0-2999). At line 14, there is another visible code that changes back to 6 lpi. Through the printing of the 4 lines at 8 lpi, we have lost 50 dots in the vertical cursor advance, which is made up at line 52 in the format.

The variable "bx" at line 22 on the format draws the ruled box that contains the invoice line items and titles. This is built on the processing table in lines 12-17. Then, at line 23 on the format, we put the line item titles into the box. You will see, to get the titles to appear in the center of the boxes vertically, the cursor had to be backed up 34 dots vertically, and the 34 dots were put back after they were printed. All of these titles were measured using the width table and then centered in their respective boxes. They did center perfectly the very first time. Not a tribute to my expertise (ha!), but a demonstration of how a little bit of planning can save you a lot of time.

Next, take a look at the variable "ia", defined at line 18 on the processing table. It is simply a 15% shaded box that is 2 lines high at 6 lpi. As we need it to print above its physical location on the format, there is a cursor positioning command included for that purpose. Also, note the use of the ^[&f0S ^[&f1S cursor push/pop commands to get the cursor back in sync with filePro's lines. We could have used the push/pop mechanism with the line item titles previously, rather than using a final ^[*p+34Y sequence. Further, the shading is printed transparently, so as not to erase the vertical lines in the box.

All of the numeric fields in the line item and total section of the invoice are aligned by cursor positioning and setting the HMI to 27 dots, which makes the space the same width as the Times 13 point numeral. Finally, on the form we have "bg" that draws the box for the total part of the invoice, and "ib", that provides the shading.

This invoice is probably as complex a format as you'll ever get into. Reports, by their nature, have far less designing required, as most of the page is repetitive line items.

Look at the form initialization codes for these forms. Both "spine" forms use code 57 which selects a landscape format at 6 lpi, while the invoice uses code 55, which is for a portrait orientation at 6 lpi. On the invoice, all of the font selections are done on the form itself, and none in initialization.

An Inexact Science

When using proportional fonts in a text field such as the "product description" field on this invoice, you can't really predict how much physical space may be required on the printed page to accommodate the field's contents, even though the maximum number of characters is known. It is almost certain on the invoice that we could enter a text string into the field that would print wider than the space allocated on the form.

If you considered each character position on average to be the same as a numeral, you'd probably never get into problems, but that may be too generous. A bit of experimentation should yield some decent guidelines. Numeric field widths can be calculated quite accurately.

Fudging the Margin in Reports

As mentioned earlier, the printer only does a page eject when a linefeed or half linefeed put the cursor beyond the bottom margin. We can still print past the margin if the cursor arrives there via a positioning command.

It is a rather trivial task on a report to put the page number at a fixed position at the bottom of the page. Essentially, we create a variable that will be placed in the heading area of the report format, which contains "push cursor { goto address { page number { pop cursor".

Say we have a report format and the page is defined with a 1/2 inch top margin and 57 lines of text. At 6 lpi, this would leave a bottom margin of 1 inch. Now we introduce a new cursor positioning command, whose unit of measure is lines or rows as opposed to dots, ^[&a###R , where ### is the row number down from the top margin. As the row numbering scheme begins at 0, the 57 lines in our format are labelled 0-56, and we want to print the page number down in the margin at line 58, which allows for a blank line.

In processing we can do something like this:

Then: ec(1,*)=chr("27"); sc(5,*)=ec{"&f0S"; rc(5,*)=ec{"&f1S"

Then: pn(6,.0)=@pn + "1";

Then: pp(35,*)=sc{ec{"*p1125X"{ec{"&a58R"{"Page"<pn{rc

Then: end

The variable "pp" is placed anywhere on the format heading, but it will be printed below the bottom margin. For some reason, if you pick up "@pn" in processing and put it in a variable on the heading, the number is off by "1", thus we set pn=@pn + "1".

Positioning the cursor vertically by "lines" rather than "dots" is preferable here, because then you don't have to calculate the exact dot position where line "58" is located. When the cursor is on the first line of a report or form, the CAP is NOT at dot position "0" vertically, but rather on the baseline of the first line, which is defined as being down from the very top 75% of the value of the current line spacing. So, the exact position of the CAP is dependent on the line spacing. Positioning by row number precludes us from having to translate the row number into dots.

This is particularly handy if you are using preprinted forms. Keep in mind still, the printer isn't counting lines. When given a row positioning command, it calculates a dot position, based on the current lpi setting, or, if you prefer, the "Vertical Motion Index" - VMI.

This technique could be used to put any kind of information at a fixed position at the bottom of the page. Page totals, maybe? If it is not possible to put the variable containing the data in the heading and it has to go onto one of the data lines, then you will have to make some creative use of "@lc", etc., to load the variable with the data and positioning values at the appropriate time. Play with this a bit, it shouldn't be difficult.

Print Code Table Review

Now, let's take a quick tour through the print code table. As stated before, the first 54 codes are functionally unchanged from the original hplaser.prt file supplied with filePro. A few of these codes have been duplicated further down the table in the interest of getting all symbol set definitions, for example, for both primary and secondary fonts, together.

Code 55-75 are intended to be the area where any user initialization codes are to be inserted. Supplied, there are four, 55-58, that do basic page formatting, while 73-75 set the number of copies. If you have a duplex printer (IIID, IIIsi), you will want to set up codes here for simplex/duplex operation and to select the bin the paper is to be drawn from. I know there are a few. There isn't much space available for additional codes, so use it wisely.

You might want to make up a couple of codes that select fonts for both the primary and secondary if you are using the same combination repeatedly. Hopefully, you can build any new code you need by nesting existing codes from the table, e.g., to create a code that made the primary font Courier 12 pitch and the secondary Times 10 pt, the new code would be defined as "%89 %101". This new code might include the symbol set, as well. Any further mention of codes that involve font attributes implies a provision for codes for both the primary and secondary fonts.

There is, though, a nasty bug in filePro that causes an error if you try to nest a print code with a value higher than 127, which precludes half the table from being nested. The problems don't appear when you define the code, but rudely show up when you try to use the code. Small Computer knows of this, and I have been told that it "will be fixed." Soon, I hope.

Continuing down the table now, 76-87 are reserved for symbol sets. Four of each are already defined but can be changed if you wish. Codes 88-97 select the fixed pitch fonts. Code 98 is simply the escape character, and you will use this one quite often if you are building your own codes directly on the output format. 99 and 100 activate the primary and secondary fonts, respectively. These are mutually exclusive, i.e., if you call code 100 for the secondary font, the primary font is inactive, and it won't subsequently be active until you issue code 99.

Selection of proportional fonts is in codes 101-128. For the LaserJet III, we only have two typefaces, so there is a bit of free space here, but for the LaserJet 4, it is pretty much full. Any of these codes select the 10 pt medium upright version of the typeface. As these fonts are scalable, we have to make the point size selection with another code (129-158). Originally, I tried to put in codes that gave you the same typeface in a variety of point sizes, but I gave up on that idea as it became apparent that there wouldn't be enough room on the table if you added a few fonts. The LJ4 adds more than a few. As our point size codes (129-158) are > 127, we can't use them nested, which is a real drag.

Similar to the point size codes, 159-166 select the pitch for fixed pitch fonts. These aren't really needed for the LJ3 internal fonts, as we have all of the possibilities covered in font selection. On the other hand, the IIIsi and the LJ4 both have scalable fixed pitch fonts, and this is the area in the table to predefine some.

This brings us to bold type, etc., at 167-175. When we use a line printer, we only think of turning bold "on or off". While this may be true with a line printer, laser fonts can have weights other than bold or medium, so we aren't dealing with an on/off situation. The codes are here for medium, semi bold, bold, and extra bold. There are others, but as we are out of room on the table, I didn't include them. These four variants will cover all the fonts on the LJ4.

The codes for font style at 176-183 show that there is more to the typeface style that italic or upright. Like the typeface weights, we have four different styles defined and these aren't all of the possibilities. They are upright, italic, condensed, and condensed italic. The LJ3 only has upright and italic fonts, while both the IIIsi and LJ4 have condensed and condensed italic fonts.

Codes 184-199 are a collection of HMI codes that relate to different numeral widths for the Times and Univers fonts at various point sizes.

Finally, the balance of the table is filled with our horizontal positioning codes. I wish we had room for more of them, but there you have it.

Omissions

One area that I have totally ignored is the "Printer Job Language" (PJL), which is used to select PCL or Postscript with the LJ4 or LJ3si printers with the Postscript SIMM installed. I don't have Postscript on my LJ4 so, consequently, my PJL Reference Manual remains untouched. If this is of concern to you, you probably want to put PJL commands in both the printer initialization and termination commands to specify PCL for use with the print code table.

There are still many, many PCL codes that are not on this table. In most cases, these require runtime values, e.g., drawing a box or other similar things that in all likelihood will be defined in processing. On Unix systems, if your version of filePro does not give you the ability to map LF to CR-LF, then you will want to put the code "$1b &k3G" into your printer initialization in code # 3. If you can see something vital that should be included, let me know.

LaserJet III

Although the printer code table printed here is complete for the LJ4, the tables for both the LJ3 and LJ4 are on the cookBook disk. If you just can't wait to get the disk, make the following modifications to the hp4.prt table.

The only mandatory changes are in codes 88, 89, 93, and 94 where you want to change "4099" to "3". Those of you who have the IIIsi should NOT make this change, as your Courier font is scalable.

The following codes are not needed: 80, 81, 86, 87, 90, 91, 95, 96, 103-111, 117-125. Leave those codes blank on your table.

LaserJet 4

From the standpoint of the print code table, the only thing with the LJ4 that should be brought up is font selection. First, the Courier and Letter Gothic fonts are scalable. The Courier typeface on the LJ3 has a typeface number "3", while Courier on the LJ4 is "4099". The Clarendon Condensed font, codes 105 and 119, is only available in condensed bold. If you have it selected and then issue any other stroke weight or style change, you will almost certainly get another typeface.

Be sure to set the printer to 600 dpi resolution at the control panel. It appears that some cahracters at certain point sizes scale to differenet widths at 300 dpi vs. 600 dpi, and the width table expects 600 dpi.

There are no font select codes for the Wingdings and Symbol fonts on the LJ4 table. As each of these fonts has a unique symbol set, simply changing to the appropriate symbol set (codes 80-81, 86-87) will get the font into action.

Width Table Bloat

While I thought it was prudent to generate the character width tables for all fonts and symbol sets, I think it is very unlikely that any one of us individually needs them all. You could reclaim a substantial amount of disk space by deleting the tables for symbol sets that you will "never" use and then restructuring the file. Study the symbol sets carefully, and then perform surgery at your own risk.

What Next

We really need a bigger print code table, one that supports maybe 1000 codes. Even doubling the number of codes to 512 would help a lot. Right now, a LaserJet 4 with an added font cartridge is already out of space on the table. I asked Small Computer if it was a possibility and was told that there would be a couple of technical issues. Primarily, an extended table would not be backward compatible with existing tables and formats, and further, memory limitations could be a concern on some systems. We can always hope.

If we are able to get a larger print code table that supports 500-1000 codes, at that time the table presented here would want to be rewritten to open up space within the table to maintain its structure and grouping of codes.

For those of you who are still using filePro version 3.0 or earlier and want to play with this, the best advice that I could give is to upgrade to 4.1., otherwise, you have a very tedious task in front of you to enter the hex codes into your print code table. You can make it work, but you may find yourself putting more codes into processing than you would with 4.1, as the 3.0 and earlier versions have fewer codes and severely restrict their length, as well.

In conclusion, I would like to point out that there is nothing definitive in the way that I have approached utilizing the PCL codes in filePro. Given the flexibility of both filePro and the HP printers, there may very well be better ways to get the job done. If you have any problems in making it work, send email or call me at work during business hours (9-5 Pacific time).