As the first step in the decommissioning of sasCommunity.org the site has been converted to read-only mode.


Here are some tips for How to share your SAS knowledge with your professional network.


Character based printing in SAS V9 on Windows

From sasCommunity
Jump to: navigation, search

Introduction

When SAS was originally ported to MS-DOS, the capabilities of printers were limited. Software programs simply sent a stream of ASCII characters to the printer. Base SAS still contains support for this kind of printing. In this article, this support will be called 'character based printing'. In SAS documentation the feature is called 'Forms Printing'. This name could be an impediment to understanding the feature.

In trying to understand this feature it is useful to recognize that the term 'Form' was used everywhere, even when some other term would have been more informative. Character based printing utilizes a collection of specific configuration information for different printer models. These configurations might have been called 'Printer Personalities' or 'Printer Profiles' or simply 'Printer Configurations'. Instead they are called 'Forms'. The sequence of panels that solicit configuration data from the user might have been called a 'DOS Printer Configuration Wizard' but instead it's called the 'FORM Subsystem'. The command that launches the panels might have been called 'DOSPrinterConfigurationWizard' but instead it's 'FSFORM'. The checkbox now labeled 'Use Forms' might have been labeled 'Character based printing for DOS printers' instead. The system option NOHOSPRINT is the exception to the implied 'name everything Forms' rule, but still isn't very clear since it defines character based printing in terms of what it isn't rather than what it is.

To perform an internet search for a printer that supports this kind of printing you won't get anywhere if you search for 'Forms Printing'. Instead you'll need to search for DOS compatible printers or find some other combination of search terms that address the problem of printing from legacy DOS applications. When so-called 'Forms Printing' is in effect for Windows SAS, it should print as if it's a legacy DOS application.

In the DOS time frame, some printer and display fonts had special glyphs for drawing lines and corners ("box characters"). Other enhancements, such as highlighted (bold) text, could be enabled by including "escape" sequences in the stream of text. Two facilities in SAS character based printing help the user exploit these printer features. One is the FORMCHAR Option. The other facility is the FORM subsystem. The FORM subsystem allowed you to specify escape sequences that exploit extended printer capabilities.

Dos and Don'ts

To continue using character based printing today in SAS V9 it is necessary to obey a few dos and don'ts.

Don't rely on the the 'Use Forms' check box found in the menus through File->Print setup. Do rely on the NOHOSTPRINT system option instead. The placement of this check box is another impediment to comprehending character based printing. According to the Windows User Experience Interaction Guidelines "Dialog boxes use radio buttons and check boxes to let the user choose from a list of options." If you run SAS with the default -NOUPRINT option, the 'Use forms' check box appears on the File->Print setup dialog box like this:

Dlgnouprt.PNG

If you run SAS with -UPRINT it appears on File->Print like this:

Dlguprt.PNG

So the user might conclude that so-called 'Forms Printing' is something that can optionally be applied to either 'Host Printing' (-NOUPRINT) or 'Universal Printing' (-UPRINT). It's really character based printing, as described earlier, and it's a different printing feature altogether than either 'Host Printing' or 'Universal Printing'. Forms that you create with the FORMS subsystem have no applicability to Host Printing or Universal Printing.

With 'Host Printing' in effect there is also a long-standing bug related to the check box. If the -HOSTPRINT system option is set you won't be able to toggle the check box on and have it stick. If you use -NOHOSTPRINT you won't be able to toggle it off and have it stick. Simply use the option and don't rely on the check box.

Don't rely on File->Print from the menus to use character based printing for the log or output windows. Do rely on the PRINT command instead.

Don't look to the menu system for a way to specify the active form, because none exists. Do use the FORMNAME command instead.

Putting it to use

So, how do we create a demo for character based printing? To begin, we need to have a target device in mind that can accept character output. Then we need to create a form that we expect will exploit the characteristics of that device (colors is a good example). Next, we need to run a SAS task of some kind that creates output that we expect will incorporate the coloring we have defined in a form. Finally, we can test the output against the target device.

Target a device

Choosing a target device on which to display the final result is not so easy these days. Many of today's printers can't accept a string of characters at all. These kinds of printers are ofter called 'Windows only' printers, or sometimes 'win-printers'. The consequences of choosing an inappropriate target can be grim. If you send a stream of ASCII text to a Windows only printer the ASCII might simply be ignored (if you are lucky) or it might jam up the printer queue with a job that can't be canceled until you restart your system. There doesn't appear to be any API available to programmatically validate the suitability of the device beforehand.

One inexpensive solution is to use the Windows Command Prompt window. SAS can't print directly to a Windows Command Prompt window, but you can print to a file and then display the file at the command prompt. There are several things you'll need to take into account to see the effects of using forms or FORMCHARS. Not all fonts support the 'box characters'. If your Windows Command Prompt has been modified to use the new Consolas font, no setting of FORMCHARS will be able to produce clean box characters. The closest you could come would be to use dashes, plus signs, and etc. You can test this right away by using the readily available TREE command at the prompt. Use either Lucida Console or the raster fonts instead of Consolas. With either of these you should see smooth lines result from the TREE command, and you can exploit them with FORMCHARS as well. The Windows Command Prompt uses codes for box characters as they were originally designated in IBM Code Page 437 The other concern is color. Your Form will dictate how colors are specified. In order for the Command Prompt Window to support escape sequences that specify color, you'll need to make a configuration change to your system. If you're running Windows XP, you'll need to change %SystemRoot%\System32\config.nt and add the following line:

DEVICE=%SystemRoot%\System32\ANSI.SYS /x

Open up a new Command Prompt Window after making this change.

In later steps you should now be able to see color differences in the SAS output file you create by issuing a command like the following:

command /c type <outputFileName.txt>

Create a form for the target device

The existing SAS documentation does a pretty good job of getting you started creating a form (a printer configuration). This section does have some issues, however. The first sentence "The FSFORM command opens the FORM window, in which you can define print forms to use when you print SAS output." would be better if it were qualified with "but only if you are printing character based output directly to a character based DOS era printer." at the end. To create a form for our experiment targeting the Windows Command prompt:

  1. Assign a libname to a path where you will permanently store your form. Enter DMLIBASSIGN and complete the dialog: Libname.PNG
  2. Launch the series of frames for defining a new form name with the command 'FSFORM udrive.myforms.console.form' . This will result in a new catalog called 'myforms' being created with a new form called 'console.form' inside it.
  3. In the Printer Selection frame, select 'Other' and press ENTER.
  4. Navigate forward through the frames using the NEXTSCR command until you arrive at the Font Control Information frame. Fill in the fields as shown here: Consfont.PNG
  5. Exit the dialog and save your work.

Generate some SAS output

Facilities like the SAS log generate text with different attributes on their own. There are indirect ways to exploit this and generate SAS output with a few different color attributes, at least.

An example follows:

 
libname udrive 'u:\forms';
option NOHOSTPRINT;
%put ERROR: This text should be RED.;
%put WARNING: This text should be GREEN.;
%put NOTE: This text should be BLUE.;
%put One last PUT to turn off blue. Else everything following would be blue.;

The first two lines simply set up a libname needed to get to persisted Forms and explicitly specifies the NOHOSTPRINT option so it isn't forgetten. The %put macro statements will print to the SAS log window in different colors simply by conventions that have been established for the keywords appearing in them. As far as I know, there aren't any other ways to 'markup' text in %put or put statements such that Forms will be involved.

At this point you can:

  1. Bring up DMS SAS
  2. Setup the libname as before.
  3. Clear the LOG window.
  4. Enter the SAS source statements above in the editor.
  5. Submit them.
  6. Select the LOG window.
  7. Enter the command 'prtfile puts.txt';
  8. Enter the command 'print form=udrive.myforms.console';

This should produce a file called 'puts.txt' in your current directory.

Test the output on the target device

Now it's just a simple matter of issuing the following command at a Windows Command Prompt:

command /c type puts.txt

Puts1.PNG

Success.

Can we output graphics to the Windows Command Prompt?

This is somewhat off topic, but at this point the reader might wonder whether Forms printing allows for producing graphics in the Windows Command prompt window using some of the techniques illustrated so far. The answer seems to be 'no'. At one time Microsoft's Anders Hejlsberg wrote a simple C# demo program that did this kind of thing. You can find it if you search the web for 'DrawBar RunTests' (no quotes). All it does is set the console colors (using the real Windows API rather than obsolete escape sequences) and output a measured number of spaces. Unfortunately, there appears to be no way to do something similar with PROC PLOT, say, or any way to do it that would exercise Forms Printing. That isn't to say it's entirely impossible.

If we take some ideas from Steve James' SUGI 26 paper Web-Application Bar Charts without SAS/GRAPH and use hard-coded escape sequences instead of his <img> tag technique, we can produce this result:

Drawbar1.png

It should be emphasized that this approach does not use the Forms facility, and in fact doesn't use any printing facility within SAS at all. PUT statements to file output are so flexible that they allow you to basically roll your own character based output, and that's all we're doing here.

Here's the modified code:

data;
input cause:$ deaths;
list;
cards;
Firearm      3794
Cut/piercing  575
Unspecified   227
Suffocation   183
Other         154
;
run;
 
%let deaths = 5075;
 
*--------------------------------------------------------------;
* Prepare data for creating graphics. The output of this step  ;
* is a SAS data set with three variables: cause, deaths, and   ;
* percent.                                                     ;
*--------------------------------------------------------------;
proc freq order=data;
  tables cause / noprint out=totals(rename=count=deaths) ;
  weight deaths;
  label cause='Cause of Injury' deaths='Number of Deaths' ;
  run ;
 
*------------------------------------------------------------------------;
* Now produce the chart int three columns: one with the cause            ;
* of death, one with the number of deaths, and one with the percentage of;
* deaths including the bar chart.                                        ;
* The bar chart is drawn using a character that fills an entire space.   ;
* Escape sequences set the color.                                        ;
*------------------------------------------------------------------------;
data _null_ ;
  file 'drawbar.txt' ;
  set work.totals end=lastob;
  if _n_ = 1
  then do ;
       put / '1b'x'[1;32m'             /* high intensity green */
           'Cause of Death             Number           Percentage of Deaths' /
           '                         of Deaths' /
                   '1b'x'[m'                   /* no attributes */
            ;
        end ;
 
  * create a numeric value for the width. We can't control the height. ;
  if percent < 1.0 then width=1 ; * set minimum width of bars;
  else width = int(percent/4) ;
  total = &deaths ;
 
  put /* print out the cause of death */
      cause $char14.
      /* print out the number of deaths */
      '        '
      deaths comma10.
 
      /* print out the percentage of deaths and the bar */
      '           '@
          ;
  put '1b'x'[31m'@;   /* red */
  do i=0 to width;
          put 'db'x@;     /* filled-in block character */
  end;
  put '1b'x'[m'@;     /* no color */
      put 
      ' '
      percent 4.1
      ;
 
    if lastob
    then do ;
     put /'Total Deaths          '
         total comma10.
     ;
 
     end ;
run ;

Printer Emulation

Because there are still legacy DOS programs out there that people need to run, a cottage industry has developed to satisfy demand for software emulators of DOS printers. Wikipedia has an imcomplete list of printer emulators. A blog posting located here has attracted an enormous number of comments about DOS printer emulators.

Forms Printing reached its zenith with the introduction of SAS/FSP products. These products were conceived in the DOS era, however, and never advanced beyond character output. For example, using only the features of FSLETTER you can't include a graphic in your letter. Current printer emulators can overcome this limitation. Using Forms, you can include escape sequences that instruct the printer emulator to include graphics.

Here is an example. First we will run a SAS job to create a small graphic. The printer emulator I'm using here prefers to work with .bmp files, and it's width and height specifications will be in centimeters. The following SAS code will drop a graph into the file system that conforms to those conventions.

filename out 'c:\images\hat.bmp';
goptions device=bmp gsfname=out gsfmode=replace width=15cm height=10cm;
 
data hat;
do x=-5 to 5 by .25;
   do y=-5 to 5 by .25;
     z=sin(sqrt(x*x+y*y));
         output;
    end;
end;
 
proc g3d data=hat;
   plot y*x=z/ctop=red;
   title 'Cowboy Hat';
   run;

Here is the Form:

Pform.PNG

Note that the form sets aside 'cyan reverse' to trigger an escape sequence that the printer emulator will use to find the graphic and include it. When typing the letter, we can use the SAS command 'color text cyan reverse', type a single '.' (a less conspicuous space doesn't seem to be enough) and then type 'color text black'. Here is our letter:

Pletter.PNG

Note that the command sequence to print is different in FSLETTER than it was in the Log window we were working with earlier. You issue the 'send' command followed by the 'end' command.

The final preview in the emulator looks like this:

Pview.PNG

This result can be printed from the emulator's preview window onto any Windows printer.

A note of caution about printer emulators: There is some ambiguity over the meaning of Newline. As the cited Wikipedia article points out, a Newline can be construed as either a line separator or a line terminator. When SAS is sending output to a printer (or a virtual printer) it uses Newline as a line separator. There is no Newline terminating the last line of a print job. If you are evaluating DOS printer emulators, be sure to consider whether you are satisfied with the way both SAS and the emulator deal with this ambiguity. In scenarios where both SAS and the emulator are installed locally, the ambiguity can be avoided if the emulator supports a file-monitoring input method, as some emulators do. The SAS job can then be modified to produce file output, in which case Newline is used as a terminator rather than a separator.