Resources

SAS® AppDev Studio 3.0 Developer's Site

Using the SAS/AF Print Manager Class to Enhance Printing of a Data Table Object

Introduction

The Print Manager class (Sashelp.Classes.Printman.class) is available to developers of SAS/AF frames so that you can print some or all of the components within a frame. Print Manager is a non-visual class controlled by methods. It is especially useful for enhancing the printed output from a data table object because it supports features like titles and footnotes. You may specify text for multiple titles and/or footnotes along with formatting properties like justification, font, and color. Page numbers may have properties of justification, position, font, and format. In addition, margins can be specified. This paper shows how to use the Print Manager to enhance printed output from a data table object.

The Print Manager class will only work on operating systems which support host printing. These include Microsoft Windows and many versions of Unix. If the operating system does not support host printing, initialization of the Print Manager will fail. If users of your application can turn host printing off, you should check that instantiation of the Print Manager succeeded before using it. If it succeeded, the object identifier will be greater than zero. You can see how to check this identifier in the code for the data table example below.

There are three basic steps to using the Print Manager. First, the add the data table object to the Print Manager's list of things to print. Next, customize the Print Manager by applying printing options. These include setting the destination to either the preview win-dow or the printer. Finally, "render" the data table. Rendering will cause the internal printing method supported by the data table to create the output. The output will be routed to the selected destination, whether for previewing or actual printing.

Adding the Data Table

To print a data table, you must add it to an internal list via the Print Manager's _addComponent method. While the only required parameter to this method is the compo-nent name, you will often want to specify some of the optional parameters. It is possible to specify how much of a component's data to print and to pass in a list of component-specific options.

The first optional parameter is printStatus. This indicates how the component should be printed in a multi-page situation. The valid values you will need are 'SINGLE' (only print on first page), and 'SCROLL' (include component on all pages and scroll the com-ponent's data for each page). The default value for printStatus is 'SINGLE', yielding a single page of output. But if the printStatus is 'SCROLL', the Print Manager will keep generating pages until the data table has printed all of its data.

The next optional parameters are alterheight and alterwidth. These take the values of one for 'yes' and zero for 'no'. These parameters usually should be one for data table components so that Print Manager will distribute the "extra" space on a page. Since the printed page is often larger than the frame to be printed, Print Manager can distribute this extra space so that more of a component's data is printed per page.

The final optional parameter is options, and it represents an SCL list identifier that will be passed to the component when it is printed. These are options specific to the compo-nent, and they are not interpreted by the Print Manager. In the case of a data table or table editor component, you might pass options that specify whether to print the current screen ("SCREEN") or the entire table ("ALL"). Telling the Print Manager to scroll the component will not be sufficient to print an entire data table unless you also tell the data table to print 'ALL'. See the second code example, below, for details.

Look at the table below to see how to manage the values of Print Manager and data table options. Note that if it will be up to the user to choose how much to print, it may be necessary for you to remove the data table component from the Print Manager in order to reset the value of printStatus. Use the _removeComponent method first, then send the _addComponent method with the desired value for printStatus. See the second code example, below, for details.

Desired ResultPrintStatusAlterheight and alterwidthData Table's Printing Area
Screen print of the data table object from the top visible row Single0,0SCREEN or ALL
Single full page of data starting from the top row visible in the data table object Single 1,1 SCREEN
Print all of the data from the top row visible in the data table object Scroll 1,1 SCREEN
Print all of the data from the first row of the table Scroll 1,1 ALL

Applying Printing Options

Set printing options by calling any of the methods supported by the Print Manager. These include methods for titles, footnotes, page numbers, and margins. The appearance of text can by customized by applying various colors, fonts, positions, and justifications. The Print Manager supports up to 40 titles and 40 footnotes, and these are addressed by the index option of the relevant methods. If index is omitted, a value of one is assumed, and the method will apply to the first title or footnote.

Setting a font is a little tricky because fonts are specified by host-specific lists. These lists are in the format returned by SCL function called Fontlist. If you wish to hard-code a font, it will be necessary to use the Fontlist function to select the font desired and then save this list out to an SLIST catalog entry. In the frame application where you are using the Print Manager, load the SLIST entry into a new SCL list, and use that list with the methods that set fonts.

Page numbers can be customized by providing some text that will be printed with the numbers. The text given with the _setPageNumberText method will be scanned for the "%" character. The current page number will be inserted into the text string at that point. For example, let's say that you wanted the word "Page:" to be printed on both odd and even numbered pages before the actual page number. Use a method call like this:

  PrintManId._setPageNumberText('Page: %', 'Page: %');

It is easy to let users of your frame application choose their own printing options. If you provide a way for them to open Print Manager's page properties dialog, they can specify their own titles, footnotes, page numbers and margins. A button, a pull-down menu, or a customized pop-up menu could be used to send the _pageProperties method to the Print Manager. That will open the dialog.

The page properties dialog can be customized. One of the first things to do is specify the default units of measurement for margins. This value is set to "inches" for sites in the United States, but may be changed to a unit appropriate for your locale. It can be readily changed to any of the valid values. These values include centimeters, millimeters, picas, points, and lines, as well as inches. There is an attribute on the Print Manager class called defaultMarginUnit, and it should be set to the unit of your choice. Do this in the Proper-ties Window or via dot notation in the frame's SCL.

The spin boxes for the margin have been set to a default maximum value of 300. Since the appropriate maximum value can be very different depending upon which units are selected, the maximum value can be changed. Use the attribute called maximumMarginValue to change this for all margins. You can change this dynamically via the frame's SCL if you want to enforce different maximums for different margin units. If the SAS System is running on a host where the Print Manager cannot change the printer's margins (such as UNIX-based hosts), then the margin settings will be added to the existing margins of the printer.

Another way of customizing the page properties dialog is by providing a supplementary dialog frame that can be opened when the user clicks on the button labeled "Options..." Give the name of the frame entry to open as the entryName parameter of the _pageProperties method. The Viewtable application uses this button to open a dialog where the user can specify the options used by the table editor class to determine the area to print and whether to print in row or column order.

You may optionally hide buttons or inactivate tabs on the page properties dialog. The hide parameter of the _pageProperties method will hide the Print and Print Preview buttons if you pass a value of "Yes". If you want to inactivate one or more of the tabs, pass the identifier of an SCL list that contains the numbers of the tabs to inactivate as the tabList parameter.

The page properties dialog may be initialized with a list saved from a prior use of the dialog. If the printlist updated by the page properties method is saved to an SLIST entry, that entry can be used to initialize the dialog in the future. Retrieve the SLIST entry into an SCL list and pass in that list identifier as the printlist parameter.

The Preview and Print buttons on the page properties dialog will cause immediate opening of the host's print preview window (if supported by the host) or cause printing to start.

Printing or Previewing

If you want your application to support both previewing and printing, you will need to call the _setDestination method appropriately. The default destination is 'preview'. Set the destination to 'preview', or 'printer', as needed. Once the destination has been set, send the _render method to get the action. (The render method does not take any arguments.) Each call to _render will generate a separate print job or opening of the preview window.

A Data Table Example

Build a frame, and make a data table object on it. Name the data table 'dtable'. Navigate the Explorer to the Sashelp.Classes catalog and drag Printman.class onto the frame. In the Properties Window, change its name to printManId, and set the objectNameUsage attribute to 'id'. Navigate to the defaultMarginUnits attribute and set the value to millimeters. Make three pushbuttons on the frame with the names 'page_properties', "print_preview", and "print". Set the label attribute for each button to "Page Properties", "Print Preview", and "Print", respectively. Add the following code to the frame's SCL entry.

INIT:
   /* Create the Print Manager class */
   import sashelp.classes.printman.class;
   dcl printman printmanId = _new_ printman();

   /* Check if instantiation failed and handle the error */
   /*  if printmanId le 0 then do;                       */
   /*      -- code to handle error condition --          */
   /*  end;                                              */

   /* Create empty list for use with Page Properties */
   dcl list printList = makelist();

   /* Create list of printing options specific to        */
   /* the Data Table. Set the Print area to 'all'.       */
   dcl list Dtoptions = makelist();
   dcl num rc = setnitemc(dtOptions, 'ALL', 'AREA');

   /* Insert the object identifiers of the data table    */
   /* and the Print Manager.                             */
   rc= setnitemO(dtOptions, dtable, 'dtable');
   rc= setnitemO(dtOptions, printManId, 'printManId');


   /* Add the data table object and pass it the list     */
   /* of printing options (dtOptions)                    */
   PrintManId._addComponent(dtable, 'scroll', 1, 1, dtOptions);
return;

Page_Properties:
  /* Open the page properties dialog and provide a frame */
  /* to open via the Options... button.                  */
  printManId._pageProperties(printlist,
         'mylib.mycat.mydtOptions.frame',dtOptions);
return;
Print_preview:
  PrintManId._setDestination('preview');
  PrintManId._render();
return;

print:
  PrintManId._setDestination('printer');
  PrintManId._render();
return;

TERM:
  rc= dellist(dtOptions);
  rc= dellist(printList);
return;

Example of a frame to call from the Options... button

Build a frame to display via the Options... button of the page properties dialog. It will provide two radio boxes for setting the printing area and printing order for the data table object. Build a frame named "mylib.mycat.mydtOptions.frame" and place two radio boxes on it.

The top radio box, called Area, should be labeled "Printing Area" and have two stations. Label the stations as "Entire table" and "Current screen only". Label the bottom radio box (Order) as "Printing Order". It will have two stations labeled "Row order" and "Column Order".

Use the object identifiers of the data table and Print Manager that are passed in on the entry list to manage the values for printing area. They will be needed for the _addComponent and _removeComponent methods. Add the following code to the frame's SCL entry:

entry dtOptionsList:list;

INIT:
  dcl num rc, char(20) printArea printOrder,
  object dtable printManId;

  /* Initialize the radioboxes if any values passed into */
  /* the frame                                           */
  if listlen(dtOptionsList) then
    do;
      printArea=getnitemc(dtOptionsList,'AREA',1,1,'SCREEN');
      if printArea = 'ALL' then area.selectedIndex= 1;
         else area.selectedIndex = 2;

      printorder= getnitemc(dtOptionsList,'MAJOR',1,1,'ROW');
      if printOrder = 'ROW' then  order.selectedIndex= 1;
         else order.selectedIndex = 2;

      /* Get the object identifiers of the data table and  */
      /* the Print Manager                                 */
      dtable=getnitemo(dtOptionsList,'DTABLE',1,1,-1);
      printManId=getnitemo(dtOptionsList,'PRINTMANID',1,1,-1);
    end;

return;

TERM:
 if _status_ = 'E'  then
   do;
    /* Get the selections from the radio boxes and        */
    /* put them into the options list.                    */
    if area.selectedIndex=1 then  printArea = 'ALL';
       else printArea = 'SCREEN';

    if order.selectedIndex=1 then printOrder = 'ROW';
       else printOrder = 'COLUMN';

    rc=setnitemc(dtOptionsList, printArea, 'AREA');
    rc=setnitemc(dtOptionsList, printOrder, 'MAJOR');

    /* Set up the Print Manager for handling print area   */
    printManId._removeComponent(dtable);

    if printArea= 'ALL' then
         printManId._addComponent(dtable,'Scroll',
                                   1,1,dtOptionsList);

     else if printArea= 'SCREEN' then
         printManId._addComponent(dtable,'Single',
                  0,0,dtOptionsList);
   end;  /* if _status_ = 'E' */
return;