Printing XCMD
Volume Number: 6
Issue Number: 7
Column Tag: XCMD Corner
Related Info: Print Manager
Printing Primer
By Donald Koscheka, Contributing Editor
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
Printing Primer
This month, I revisit the wonderful and often misunderstood world of printing
from Hypercard. I can’t imagine how much effort is spent generating simple reports
for users, but the other day, I was working with a PC programmer who was absolutely
enthralled with the fact that he could draw boxes around paragraphs of text in a report.
As Mac People, we tend to take aesthetics for granted, yet scores of otherwise great
programmers simply lose it when it comes to printing. This month, I will present a
“printing primer” for Hypercard and hopefully you will be able to use these ideas in
your own stacks.
Printing is as simple as drawing to the screen, only harder. The added
complexity stems from the fact that printing requires page management -- page
breaks, physical page layout and the like.
One approach to page management that you might find useful is to create a series
of report generation commands, a mini-report program generator, if you will. You
generate reports by issuing a sequence of commands from HyperTalk. Listings 1 & 2,
“ReportUtils.c” , exemplify such a scheme. ReportUtils is a collection of generic
printing routines that you can use in your XCMDs as well as in applications that you
create from scratch. ReportUtils consists of two parts: (1) parsing code to translate
commands from the input stream and (2) formatting code that allows you some
modicum of control over the report’s layout (you should enhance the code to satisfy
your particular criteria). Due to space limitations, I can only offer a sampling of
what this report generator can do. Beyond that, please read the source code for details.
Printing is easy and like all programming jobs, the best way to learn it is to do it.
Creating a report now becomes a simple matter of executing the report
generation commands in the correct order. You start by invoking the command
“BeginReport( listing 3 ). BeginReport initializes the report generation environment
and creates some resources that will be needed by the other XCMDs in this suite. As
long as the report remains open, you can send the report a series of formatting and
printing commands. I have provided some examples in the following listings but it
should also be fun to create your own suite of commands. When you want to close the
report, you invoke the XCMD, “EndReport” (listing 4).
In Macintosh parlance, opening a report means opening access to the printer
driver, creating a job record and opening a printing document. BeginReport tracks all
of the data needed for the report generator in a special “print record” (see listing 2).
which it places in the system heap. Note that Begin report puts up the print dialog so
that the user can exercise some control over the print job. This step is optional; you
can initialize the job dialog yourself and bypass the user interface as MPW does.
Bypassing the user interface allows you to print multiple documents without user
intervention. If you are writing the world’s greatest word processor, please make this
an option for your user.
The special record alluded to earlier is declared as struct of type pInfo in
ReportUtils.h. All information about this print job is stored here and you can add
fields to this record as needed. I store this information in the system heap or in the
system resource file so that any XCMD can get at the information quickly. With
arguing this approach on its merits, it is an effective method for sharing information
among several XCMDs. I keep track of rather mundane stuff -- where the pen is on the
page, the page margins, the current drawing style (curntStyle), the default style and
foot noting information. Of course, we also store the print Record, the status record
and the printer’s grafPort as this information will be needed by the print manager and
quickdraw.
The important thing to keep in mind about the pInfo record is that any XCMD in
the reporting suite can access it because BeginReport stores the record as a resource
in the system file with the call to addSystemResource (published previously). Any
XCMD that wishes to participate in the report generation need only get a copy of this
handle, set the port to prPort and manage the other fields in the record.
BeginReport initializes the fields in this record and opens up the document which
will be closed in one of two ways: (1) EndReport is invoked or (2) the maximum of
128 pages in a document is exceeded in which case the current document is closed, the
page count is recycled (totalpages) and a new document is opened. Tracking the
document pages independently of the printed pages ensures that page numbers don’t
print modulo 128.
HyperReportMaker allows you to set up footnotes with up to 4 fields on up to two
lines. Each line contains two fields, one on the right side of the page, the other on the
left (I call this “split” justification because I don’t know the printer’s term for a line
where the center is left blank).
As you print text, you might want to change the style. This is done using the
SetStyle command (listing 5). Printing is fairly routine and is handled by
PrintParagraph (listing 6) and PrintTitle (listing 7). A sample control XCMD is
shown in EjectPage (listing 8) which shows how to force a form feed at any time
during printing. This is useful when you want to include data for one card on each page.
Once the information for a given card is printed, eject the page and move to the next
card. HyperReportMaker includes XCMDs for setting margins, printing headers and
printing footnotes. Unfortunately space precludes including these in the column. If
you need the code, get this month’s source disk. If you can’t wait for the source disk,
you should be able to wire the code together from ReportUtils.c.
When you are done with the report, you shut it down by invoking endReport. This
XCMD shuts down the current page and document, executes the spooler (PrPicFile) if
needed and removes the printing resource from the system resource file. BeginReport
and EndReport must therefore bracket all of the commands that you wish to use to
compose a report from a Hypertalk script. Invoking a report command outside of this
bracketing won’t cause an error but it won’t do anything either.
There are several ways that you can embellish this code -- try adding a command
to print pictures or to add page frames or to improve any of the listings. By all means,
please experiment. Read the code and try to find out what the limits are, and then break
them!
One last note: I’m hosting a new service - an XCMD programmer’s SIG on
America-On-Line. I’m available to answer questions about my code (but rarely to
defend it). Stop by for a visit or an informal chat. My address is “AFC Donald”.
Listing 1: PrintUtils.h
/*******************************************
* file: PrintUtils.h *
* version: 1.0B *
* ---------------------------------------- *
* By: Donald Koscheka *
* Date: 30-OCT-89 *
* © Copyright 1989, Donald Koscheka *
* All Rights Reserved *
\*******************************************/
/********************************************/
/* -- CONSTANTS -- */
/********************************************/
#define CARD TRUE
#define BKGND FALSE
#define NULL 0x00
#define TAB 0x09
#define CR 0x0D
#define LF 0x0A
#define FF 0x0C
#define QUOTE 0x22
#define COMMA 0x2C
#define PERIOD 0x2E
#define PAREN 0x28
#define TIMES 20
#define DEFAULT_FONT 20 /*** Times ***/
#define DEFAULT_SIZE 12
#define PLAIN_TEXT 0
#define teJustFill 2
#define PAGE_INFO ‘pifr’
#define PAGE_ID 128
#define TEXT_JUSTIFICATION 12396
#define TEXT_STYLES 12549
#define STRING_TYPE ‘STR#’
#define TEXT_SIZE 10
enum{ /*** different device types ***/
Screen = 1,
Printer,
};
/********************************************/
/* -- TYPE DEFINITIONS -- */
/********************************************/
/*** This structure is used internally by ***/
/*** the printing routines. Û***/
typedef struct styleSet{
short Font; /*** current font ***/
short Size; /*** current font size ***/
short Style; /*** current font style ***/
short Just; /*** current justification ***/
}styleSet, *stylePtr;
/*** footnote ***/
/*** this is a study piece only we’ll want to embellish this concept
***/
typedef struct{
Handle key1;
Handle key2;
Handle key3;
short hite; /*** if zero, no footnote set ***/
}foot, *footPtr, *footHand;
typedef struct{
short portType; /*** RESERVED FOR FUTURE EXPANSION***/
GrafPtr prPort; /*** pointer to the display port ***/
Point curntPen; /*** where we currently drawing ***/
Rect margin; /*** “local” paragraph margin ***/
styleSet curntStyle; /*** current style settings ***/
styleSet defaultStyle; /*** default style settings ***/
short pagecount; /*** number of the current page ***/
short totalpages; /*** total pages printed in doc ***/
/*** following will go in a union ***/
/*** so structure can be used for ***/
/*** for screen drawing also. ***/
THPrint prRecHandle; /*** Handle to printing rec. ***/
TPrStatus myStRec; /*** print style information ***/
foot footNote;
}pInfo, *pInfoPtr, **pInfoHand;
/********************************************/
/* -- FUNCTION PROTOTYPES -- */
/********************************************/
long matchToken( Handle buf, short tabl );
long parseNum( char *bp );
char *nextToken( char *bp );
void parseRect( Handle buf, Rect *theRect );
void PrintString( pInfoPtr pp, Handle theText, short hite );
void PrintParagraph();
void CheckPageBreak();
void SetMargin( pInfoPtr pp, short t, short l, short b, short r
);
void DrawTextRun();
void SetFont( stylePtr sp, short theFont);
void SetFontSize( stylePtr sp, short theSize);
void SetFontStyle( stylePtr pp, short theStyle);
void SetFontJust( stylePtr sp, short theJust);
void SetFontStyles( pInfoPtr pp, stylePtr sp );
Listing 2: ReportUtils.c
/*******************************************
* file: ReportUtils.c *
* version: 1.0B *
* Common routines for printing reports *
* ---------------------------------------- *
* By: Donald Koscheka *
* Date: 30-OCT-89 *
* © Copyright 1989, Donald Koscheka *
* All Rights Reserved *
\*******************************************/
#include
#include
#include
#include
#include
#include
#include
#include
/****************************************************/
/* -- MISCELLANEOUS UTILITIES -- */
/****************************************************/
short getStringHandleSize( h )
Handle h;
/******************************
* In hypercard, strings are padded
* out to even boundaries so that the
* handle size may be longer than
* you’d like.
* This routine returns the length
* of the string up to but not
* including the NULL
* This routine is to be used for
* NULL terminated strings only!
*******************************/
{
short i = 0;
char *endPoint;
if( validHandle( h ) ){
i = (short)GetHandleSize( h );
endPoint = *h + i - 1;
while( *endPoint ){
--i;
--endPoint;
}
}
return( i );
}
short strNComp( s1, s2, len )
char *s1;
char *s2;
long len;
{
short indx;