Right-Justify Form Letters

By Naseem F. Saab

Creating applications is getting more challenging because end-users are continuously exposed to features available in other applications. Since the DPS Data Management System contains a text editor and a mail-merge feature for outputting form letters, clients often ask us to incorporate features that are available in full-featured word processing applications.

Some of the requested features like snaking columns and a graphic page preview (as found in WordPerfect) are beyond the scope of DPS or too slow when implemented with Clipper code. On the other hand, when we were recently asked to allow form letters to be printed right-justified we decided to accept the challenge.

The first issue was how to implement the feature. Since form letters are usually printed flush left and ragged right, we had to give the end-user a means of telling us which form letter is to be printed right-justified. But right justification usually applies to a specific area of the form letter -- as in the body section -- and not the full letter. So we decided to allow the user to place a marker within the form letter indicating where right-justification is to take effect and where it should be canceled. The commands of choice were: "{JUSTIFY_ON}" to start justification and "{JUSTIFY_OFF}" to cancel it.

Our original mail-merge routine parsed the contents of the form letter performing field insertion and word-wrap and then sent the resulting text to the output device a line at a time using the command:
? L_Line_Out

To allow the mail-merge routine to handle the justification on demand we added the logical memory variable "L_Just_On" with a default of false. We also added two Case conditions to the command parser of the mail-merge routine:

*---turn justify on
CASE UPPER( L_Input ) = "{JUSTIFY_ON}"
  L_Just_On = .T.
*---turn justify off
CASE UPPER( L_Input ) = "{JUSTIFY_OFF}"
  L_Just_On = .F.

and finally modified the "?  L_Line_Out" command to:
? IIF( L_Just_On, MrgJustify( L_Line_Out, L_Width ), L_Line_Out )

Note: The L_Width memory variable contains the difference between the right and left margins.

The second issue, of course, was to write the justification function. We had the string to be outputted -- we just needed to insert enough spaces to expand the string to a fixed width.

We will now explain the contents of the MrgJusitfy() function grouping the explanations by the line numbers listed in the source code:
<B>3<D> Removes trailing blanks from the text string. Leading spaces are preserved for paragraph indentation -- they are not considered part of the text string that is to be expanded.

<B>4 - 6<D> Defaults the width to 80 if none was specified. For FoxPro, use the Parameters() function or check the Type() of P_Width verifying that it is numeric. Note: Lines 3 thru 6 are not used in the DPS source code -- since the string is always trimmed by the mail-merge routine and the width is always specified -- they are contained here for completeness but eliminating them would speed things up.

<B>7<D> For the text string to be expanded to a fixed width three conditions must be met: 1- The string is longer than 2/3 of the fixed width -- this is needed so that a short last line of a paragraph would not get justified. 2- The width of the string is less than the justified width to be achieved -- if the string happens to be the exact width, then we do not have to expand it. 3- At least one space is contained within the string excluding any leading spaces -- this condition should be true <I>most<D> of the time but we have to account for it (sure the line would not be justified, how can it, but at least the application won't blowup if someone places garbage text in the form letter.) If any of these conditions are false, line 24 is executed and the string is returned as is.

<B>8<D> Counts the number of leading spaces and saves that number to P_Extra.

<B>9<D> Removes any leading spaces from the text string -- since they should not be expanded, to preserve paragraph indentation.

<B>10 - 11<D> The P_Search & P_Repl are used to make the expansion of the string uniform. They will be incremented by one space each within the DO WHILE at lines 15 & 16.

<B>12<D> This starts the loop that will only be exited on line 19 when the text string has been expanded to the specified width.

<B>13<D> Calculates how many spaces need to be inserted to achieve the fixed width for the text string. Note: We have to take into consideration the number of leading spaces (P_Extra), since we have to put these spaces back into the resulting string.

<B>15 - 16<D> Increment P_Search & P_Repl by one space each. As such, the first time into the DO WHILE we will end up replacing any single space with two spaces, the next time we will be replacing any two spaces with three spaces... This results in a uniform expansion of the text string until the fixed width is achieved.

<B>17<D> This is the main workhorse (you probably expected something more complex.)  This "one-liner" does the job since the difference between P_Search & P_Repl is always one space and since P_Need contains the number of spaces that need to be added, the STRTRAN() function will never insert more spaces than required -- if it inserts less than required, that would be taken care of the next time through the DO WHILE.

<B>19<D> Exits the DO WHILE when the total width has been achieved.

<B>22<D> Attaches any leading spaces, that had been preserved, to the expanded text string.

<B>24<D> Returns the final text string.

The full implementation took less than three hours and a free upgrade, containing the new feature, was mailed to the unsuspecting client the next business day -- we make it a policy to encourage our clients to suggest features for future releases of DPS by sending them a free upgrade containing their suggested feature(s) when it is implemented. A few days later the client called showering us with praise -- I can safely assume that she is one happy camper.

So make <I>your<D> clients happy by implementing the MrgJustify() within your application. All we ask is that the Author name and Copyright notice be kept within the function.

Now if I can only figure out how to print form letters right-justified with proportional fonts, I'll be a happy camper too.



About the author: Naseem F. Saab, an Electro-Mechanical Engineer, is President of Data Processing Systems (DPS), a consulting and programming firm that was established in 1985 and is located in Reston, Virginia. He has written, markets and supports the DPS Data Management System, a contact & business management software with a world wide client base. He can be reached at (703) 860-5022.