GDI printing opens up the possibility of replacing pre-printed forms with computer-generated forms-on-demand (created on plain paper at the same time as the data that is printed on the form). This can result in significant convenience and cost savings, but there are choices to be made and possible obstacles to be overcome on the road to implementation. The remainder of this topic is an attempt to provide some guidance on the subject for anyone thinking of going down this path.
Scanned vs manually "drawn": There are two basic approaches to reproducing the existing pre-printed form: scanning it, or recreating it with GDI primitives. For simple forms, recreating the layout with primitives such as
//RECTANGLE ,
//LINETO , and
//TEXTOUT , etc., has the advantage of minimizing the potential difficulties related to aligning the form data with the form background. Since you must explicitly position the various boxes which make up the form, there is no doubt as to how to explicitly position the data to go in those boxes. Plus, the form can be easily adjusted over time (unlike a scanned form which may require re-scanning or treatment in an image editor to make changes).
However, in order to successfully recreate a form from GDI primitives, the form has to be simple enough to consist of only lines, boxes, ellipses, polygons, uniformly shaded areas, and of course text. In addition, images (such as logos) can be accommodated using the
//IMAGE directive (provided you can supply the artwork in one of the supported file formats). Some forms may simply be too complicated to recreate this way. (Or it may be possible but require too many hours with a ruler and trial-and-error.) Note that if you go this route, a utility allowing you to view the report on the screen, such as
APEX , will be invaluable.
Scanning the entire form into a single image file has the advantage of giving you essentially a perfect copy of the form while eliminating the time otherwise required tinkering with individual GDI directives to render the individual elements of the form. (In particular, forms with very small tightly-spaced text can be particularly difficult because of the sometimes impossibility of reproducing the exact inter-character spacing that was used on the original form.)
If you choose the scanning route, the following tips can save you a lot of frustration down the road:
- Make sure your form is perfectly aligned on the scanner (or use some software to de-skew the result). The slightest skew will become obvious when the horizontal and vertical lines in the form are printed.
- Use sufficient resolution to avoid chunky dithering effects. (200 dpi may be sufficient for most forms, but if there are rounded edges of graphics, you may need to go to at least 300 dpi to avoid stair-stepped edges.)
- Trim (crop) at least 1/4" of border space around the scanned image, so that the image size at 100% is no larger than the printable area of the typical page/printer. (Most laser printers, for example, have a built-in hardware margin of about 1/4", so that the actual print area is about 1/2" smaller, in each dimension, than the physical paper size. (This hardware margin may vary slightly between printers, but 1/4" is probably safe.)
- Calculate the right coordinates for your //IMAGE statement: To render the form on the page, you'll use an //IMAGE statement (at the top of each page). Getting the coordinates right requires a bit of arithmetic which may not seem critical but will go a long way towards both simplifying your task of aligning the data on the form, and making the form truly device independent.
Ideally, you want the scale of the computer-printed form to match the pre-printed form exactly. (This should allow you to preserve your existing spacing when printing the data on the form.) To accomplish this, you must make the size of the rectangle specified in the //IMAGE statement equivalent to the size of the scanned image (normalized back to the same real-world dimensions).
An example will make this clear. Let's assume your original form is 8.5 x 11 (letter), and that we can afford to crop 1/4" off all sizes (just white space being lost). We scan the original at 200 dpi, and then use some kind of editor to crop it. After saving the image as a JPG, we see that the exact dimensions of the saved image are 1550 x 2050 pixels. (At 200 dpi, this is equivalent to 7.75 x 10.25 inches, meaning that we actually cropped a little more than we needed to, but that's ok - it's better to error on the small side, as long as we didn't lose anything important in the cropping operation.)
If we use a
map mode of LOENGLISH (units of 0.01 inches), our //IMAGE rectangle them becomes simply:
//IMAGE,filespec,0,0,775,1025
Note that it's ok, and indeed preferable, to use 0,0 as the upper left corner - we'll deal with aligning that to logical upper corner of the page using the XORIGIN and YORIGIN (below).
But first, let's consider the case where it is impossible to match the IMAGE rectangle exactly to the physical scanned form size. This can happen for one of two reasons:
a) The original form has printing that goes right to the edge (so we can't just crop off 1/4" on all sides).
b) The original form is an odd size (say, 9 x 12), and we'd rather render it in a similar but standard size, like LETTER.
In either of the above cases, we'll have no choice but to scale the scanned image. This will complicate the task of aligning the data to the form, but we can still make it easier and device-independent by maintaining the aspect ratio. Taking the example of a 9 x 12 form, with printing right to the edge so we can't crop it, we'll end up with an image (assuming 200 dpi) of 1800 x 2400 pixels. That's an aspect ratio of .75 (width/length). The maximum print rectangle on letter sized paper for most laser printers will be 8 x 10.5, which is an aspect ratio of .76. Not exact, but close enough that we can make it exact by just increasing the horizontal margins slightly. 10.5 (the printable paper length) x .75 (aspect ratio of the image) gives us 7.875 inches wide. In other words, if we maintain the original .75 aspect ratio and shrink the image to fit within the typical printable area of a letter-sized page, we'll end up with .125 inches extra margin on the side (which we can split between the two sides, or put all on one edge).
Splitting it roughly in half (now using HIENGLISH, or .001 inch units), we might use:
//IMAGE,filespec,063,0,7937,10500
Our spacing (for the data) will have to be adjusted to fit the scaled-down printed image of the form, but at least we've maintained the aspect ratio (so it won't look distorted), and it should print consistently on any printer (provided it supports at least 8 x 10.5 of printable area on the page).
Depending on the form and the difference in aspect ratios, you might decide that it is better to fill the page as much as possible by stretching the image to fill the maximum print rectangle, using the IMGF_STRETCH option. That's perfectly fine too, as long as it doesn't look funny. In either case, whenever there is stretching or scaling, you'll need to re-engineer the spacing of the data to match the form (more about that in a minute).
First, it's time to deal with the fact that not every printer has the same hardware margins. Although 1/4" is fairly standard, it's quite possible that it could be slightly less (or more, although I haven't seen that). And it is not unusual for the side margin to be slightly different than the top margin. To get the form reliably and consistently positioned on the page, the trick is to use the
XORIGIN and YORIGIN GDI directives (or the equivalent
Printer Init Commands ). Assuming we can trust 1/4" as being sufficient, use:
//XORIGIN,360
//YORIGIN,360
(Units for these directives are always in twips, or 1440 to the inch). The effect is to set the 0,0 position (used as a basis for all of the other GDI directives) to a point 1/4" from the top and left sides of the physical page (provided that is not less than the actual physical margin of the printer, which explains the disclaimer of assuming 1/4" is sufficient). So the rectangle of 0,0,775,1050 in the IMAGE statement will be effectively shifted down and to the left by 1/4". Using XORIGIN and YORIGIN to perform this shift is easier than doing it individually on every GDI statement.
Finally, we come to aligning the data to the form. If you managed to render the form at exactly the same scale as the original, then you probably don't need to change your print spacing logic at all. But if you had to resort to scaling, or you are switching from a dot-matrix printer to a laser, resulting in a different font, line spacing, etc., then here's what you must do:
1) Print out a sample form with sample data
2) Use a ruler and calculate the necessary vertical spacing (lines per inch) needed. For example, you may have originally used 6 LPI, but now the form is slightly smaller than the original, so you need something more like 6.5 LPI. Convert that to spacing from one line to the next. 6.5 LPI is .15384 inches per line. That's a very awkward number, so we may need to switch to a more precise mapping unit. Even in twips, that's 221.54 twips per line. HIMETRIC (.01 millimeters) is the most precise, and even there we get 390.8 per line, but rounding that to 391 will probably be precise enough. So:
//;use units of 1/100 mm (1/2540 in)
//SETMAPMODE,HIMETRIC
//;set spacing to achieve approx 6.5 LPI
//SETVMI,391
3) Make the same kind of calculation for the horizontal spacing. Let's assume your original was using 10 CPI, but now because of scaling you need to shrink that to 10.6 CPI. We can adjust the CPI of the printout in one of two ways, either via the
PITCH =AUTO and
CPP statements in the printer init file, or via the
width parameter of
//SETFONT . The former requires converting the desired CPI (characters per inch) to CPP (characters per page width), which requires knowing the exact logical width of the page. We have been assuming a logical/printable width of 8 inches, so 10.6 CPI would be 10.6x8 = 84.8, which we can round to 85 CPP. So in this case we could add to our printer init file:
PITCH = AUTO
CPP = 85
Using //SETFONT, the units are tenths of points (720/inch). So 10.6 CPI would be a character width of 67.9, which we'll round to 70. We'll also need to set an appropriate font height (something that fits in 6.5 LPI). Again the units are tenths of points, so 6.5 LPI is about 110 units. So our SETFONT directive might be something like:
//SETFONT,110,Courier,FIXED_PITCH,ANSI_CHARSET,FW_MEDIUM,FS_UPRIGHT,0,70
We'll have to insert the //SETMAPMODE, //SETFONT, and //SETVMI statements into the top of the printfile, plus a //IMAGE statement at the top of each page (unless we download the image into the printer as a macro, but that's outside the scope of this discussion). Note that //SETFONT does not change the vertical spacing set by //SETVMI, so we could adopt a slightly smaller font height (100 vs 110, i.e. 10 points vs 11 points), without changing the alignment.
With those adjustments, we should be able to get the spacing of the printed data to match up with the spacing of the form. But we may still need to shift all of the text horizontally or vertically to align with the form (equivalent to adjusting the paper position in the dot matrix printer). You can do that with the
XOFFSET and YOFFSET directives. These are similar to the XORIGIN and YORIGIN, but they do not affect the position of the image.
So for example, if you want to shift all of the text over to the right by 1/2 inch, and down by 1/3 inch, you can add:
//XOFFSET,720
//YOFFSET,480
Of course, you can also insert //MOVETO directives to reposition the "cursor" on the page, or position each token of text with an individual //TEXTOUT command, but the above techniques should allow you to adjust the spacing of your existing plain text (by scaling and/or shifting it in both dimensions) to match the form. And if you followed the guidelines about getting the //IMAGE rectangle to fit within the logical area of the page on every printer you are interested in printing on, you should be able to get near perfect device independence (i.e. should be able to print the same printfile on different printers with the same results).
And it was all accomplished with minimal changes to the existing program (just adding a few //GDI commands to the printfile). You might even be able to eliminate those changes and leave the original program untouched, by using features in the printer init file instead of the //GDI directives inserted into the print file. For example, the GDI directives inserted at the top of the print file could instead be put into a separate file referenced by the
PREFIX statement. And the //IMAGE statement to render the form at the top of each page could be put into another separate file referenced by the
OVERLAY statement.
So you have a pretty good set of tools in A-Shell/Windows for converting old-fashioned dot matrix reports on pre-printed forms into more cost-effective, convenient, and possibly better looking laser forms, with minimal (or in some cases, no) changes to your existing report programs. The key though is to be aware of the issues relating to the difference between the physical and logical/printable paper size, and to be systematic in your approach. If you just use trial-and-error to get it working on your in-house laser, you may find that it isn't quite aligned on your customer's printer, leading to a lot of extra work, confusion, and frustration for all involved.