TweetFollow Us on Twitter

XCMD Printer
Volume Number:4
Issue Number:10
Column Tag:HyperChat®

XCMD Cookbook

By Donald Koscheka, Apple Computer

Print Manager Access

Printing may not come to mind as an area where writing an XCMD might be useful since HyperCard prints card reports. Perhaps you don’t like the reports that HyperCard prints out, or you want to set your own margins or draw borders around each field that’s printed out. XCMDs provide access to the ToolBox Print Manager, empowering you with the capability of performing custom printing in HyperCard. The Print Manager, then, becomes a good candidate for XCMD exploration.

Reporter.P

This month’s XCMD, Reporter.P ( see Listing 1) is a simple text printer that you can adapt to your own needs or use as a template for further exploring Macintosh Printing. As always, we start with the XCMDBlockPtr as the interface to Hypercard. We pass a handle to the text in the first parameter. We could use the other parameters to pass formatting information. Parameter 2, for example, might be set to tell us to draw a border around the page. Parameter 3 might contain the margins in page coordinates.

The algorithm we use goes like this: Move to the top of the page and initialize a rectangle, lineRect, whose height is the height of the text to be printed and whose width is zero. Step through the text to be printed accumulating words (text separated by “white space” or punctuation). If the width of this word plus the width of linerect is less than the page width, add this line to the current line; otherwise, move down to the next line and add the current word to the start of the next line. If any part of the next line is below the bottom margin of the page, eject the page and reset lineRect’s top to the top of the page.

Scoping

One of the nice features of writing XCMDs in Pascal is the scoping feature of procedures and functions. Pascal allows us to define procedures and functions within the main procedure, in this case, the procedure “Reporter”. This is useful because it allows the callbacks and the nested procedures to share reporter’s local variables. When we invoke a callback, we don’t need to pass the parameterBlocPointer. The callbacks can “see” this pointer as if it was a global data declaration. Another nice feature of writing XCMDs in Pascal is that we don’t have to be fussy about the entry point. The variable scoping simplifies the compiler’s job of resolving the entry point. In “C”, subroutines follow the main body of the XCMD so that the compiler can use the start of the main body as the entry point.

Step Through

Reporter first checks the parameter block pointer to determine whether enough parameters were passed. Params[1] should be a handle to some text to print. If the handle is NIL, we don’t have anything to print; otherwise, we open the printer driver, allocate a printer record handle (prRecHandle) and fill the record with the print defaults. If you wanted to allow the user to change the page style (e.g. page orientation), this is where you would invoke the printer’s style dialog. We’ll skip this step for now because we want the XCMD to print the document with a minimum amount of intervention from the user.

Next, we put up the job dialog which is the standard printing dialog that comes up when you select print from the menu. If PRJobDialog returns true, the user clicked the “OK” button and wants us to print the document.

PROpenDoc associates our printing with a grafport. This technique effectively hides the gritty details of printing from the application. As long as we draw to the grafPort returned by PROpenDoc, the underlying code will convert our QuickDraw commands to Postscript for the LaserWriter and to a ribbon-eating bitmap for the Imagewriter.

Once the grafport is opened, we open the first page of the document with the PROpenPage and call PrintHandle to print the text. PrintHandle manages the opening, closing and ejecting of successive pages so when it returns all we need to do is close down the printing port with the PrCloseDoc call. Understanding this routine will take you a long way to understanding the how this printing business works.

Once we’re done printing, we check to see if the document needs to be spooled out. Spooled files are saved to disk and printed by the call to PrPicFile. This statement insures compatibility with print spoolers that will be looking for picture files to print.

The actual printing is performed by the procedure PrintHandle. For the sake of argument, we set the font to nine point Geneva. A better approach would be to pass font information as a parameter to the XCMD. GetFontInfo tells us the height of the text since that will also be the height of a line on the page. We then calculate some rectangles and set the coordinates of our lineRect variable. Note that lineRect starts off at the top left of the page and has an initial width of 0. LineRect will expand to include the width of each word that we add to the line.

Locking down the text handle allows us to maintain pointers to the text. The first pointer, cpos, points to the current position in the text while wpos is used to point to the first character in the current word. A third pointer, tpos, is used as a “test pointer” and will remember the end of the current word.

The repeat loop in printhandle points tpos to the start of the next word (or the first character in the text if we’re just starting the loop). If tpos is currently pointing to a line termination character (e.g. carriage return), we print the current line, move to the next line and reset wpos to point to the next word. Form feeds are handled in a similar fashion except that we eject the current page before continuing.

If tpos points to the nilChar (the ASCII character whose value is 0), we’ve printed all of the text in the input stream so we print the line, eject the page and exit the loop.

The otherwise clause checks to see if the next word in the input will fit on the current line. If so, add the word to the line and update the pointers accordingly. Since tpos points to the end of the current word, setting wpos equal to tpos causes us to “leap” over that word.

If the word doesn’t fit on the line, we first print out the line and then move to the next line. After a word is “accumulated” into a line, we add the width of the word to the linerect.

The last routine of note, CalcNextWord, checks to see if the next character in the text is a word break character such as a space or punctuation. If so, we now have another word to add to the output line.

Summary

Reporter left justifies the text but you can easily add full justification. When you get to the end of the current line (i.e. wordsize + linerect > page width) calculate the number of pixels that would be needed to “fill” out the line. Subtract the current line’s width (right-left) from the page width. If the current line is 2000 pixels wide and the page rectangle is 2500 pixels, then the number of pixels needed to fill out the line is 500. Divide this number into the number of spaces on the line and call SpaceExtra to space out the text.

You’ll be surprised out how easy text formatting is with the Macintosh toolbox and I encourage you to use Reporter as a starting point for your explorations.

{********************************}
{* File: Reporter.p*}
{* *}
{* Prints the entire *}
{* contents of the container*}
{* *}
{* ------------------------ *}
{* In:  params[1] = handle  *}
{* to the text to be printed*}
{* *}
{* ------------------------ *}
{* © 1988, Donald Koscheka, *}
{* All Rights Reserved    *}
{* ------------------------ *}
{********************************}

(****************************
 BUILD SEQUENCE

pascal Reporter.p
link -m ENTRYPOINT -rt 
XCMD=6555 -sn Main=Reporter 
Reporter.p.o 
“{Libraries}”Interface.o 
“{PLibraries}”Paslib.o 
-o “{xcmds}”testxcmds

*****************************)

{$S Reporter }

UNIT Donald_Koscheka; 

{----INTERFACE----}

INTERFACE

USES
 MemTypes, QuickDraw, OSIntf,
 ToolIntf, PackIntf, HyperXCmd,
 PrintTraps;


PROCEDURE EntryPoint(pPtr:XCmdPtr);

{----IMPLEMENTATION----}

IMPLEMENTATION

{$R-}   
CONST
 CARD   = TRUE;
 BKGND  = FALSE;
 NILCHAR= $00;
 CR= $0D; 
 TAB    = $09;
 SPACE  = $20;
 FF= $0C; 
 LINEFEED = $0A; 
 QUOTE  = $22; 
 COMMA  = $2C; 
 PERIOD = $2E;
 PAREN  = $28;
 
TYPE
 Str31 = String[31]; 

PROCEDURE Reporter(pPtr:XCmdPtr);FORWARD;

{----EntryPoint----}

PROCEDURE EntryPoint(pPtr: XCmdPtr);
BEGIN
 Reporter(pPtr);
END;


{----Reporter----}

Function CalcNextWord(VAR wPtr:Ptr):INTEGER;
(**********************
* Given a pointer to a 
* word, calculate the 
* length of the next word
* in the run.  The length
* is determined by adding
* the width  of each character
* together until a word
* break is hit.
*
* The difference between 
* tpos and wpos is always
* one word (including the 
* sticky characters)
*
* IN: Pointer to text 
*wPtr == pointer to NEXT word in
*run (or NIL if No next word)
*
* OUT:  Width of the next 
* word in the line
***********************)
VAR
 done : BOOLEAN;
 wLen : INTEGER;
BEGIN
 done := FALSE;
 wLen := 0;
 
WHILE NOT done DO
BEGIN CASE wPtr^ OF

CR,FF,LINEFEED,NILCHAR:
 done := TRUE;
TAB:
 BEGIN
 WLen:=wLen+CharWidth( chr(SPACE) );
 done := TRUE;
 END;
 
 SPACE, QUOTE, COMMA, PERIOD: 
 BEGIN
 wlen:=wLen+CharWidth( chr(wPtr^));
 done := TRUE;
 wPtr:=Pointer( ORD(wPtr) +1);
 END;
 
OTHERWISE
 BEGIN
 WLen:=wLen+CharWidth( chr(wPtr^));
 wPtr := Pointer( ORD(wPtr) + 1 );
 END;
END; {*** CASE wPtr^ OF ***}
END; {*** WHILE NOT done ***}
 
IF wPtr^ = 0 THEN wPtr := NIL;
 
 CalcNextWord := wLen;
END;


Procedure PrintHandle( hand : Handle; printer : TPPrPort; Prec : THPrint 
);
(**********************
* Print the data passed 
* as a handle and 
* using the given printer
* port.
*
* Prints the data into 
* the current port and
* handles word wrap and
* page breaks.
*
**********************)
VAR 
 done   : BOOLEAN;
 lineHite,
 wordSize : INTEGER;
 pageHite, 
 pageWidth: INTEGER;
 num    : INTEGER;
 cPos   : Ptr; 
 wPos   : Ptr;
 tPos   : Ptr;
 lineRect : Rect;
 fontInfo : FontInfo;
 
PROCEDURE EjectPage;
(****************************
* Eject the current page and 
* adjust the rectangle 
* accordingly
*
****************************)
BEGIN
 PrClosePage( printer );
 PrOpenPage( printer, NIL );
 
{*** Opening a new page yields***}
{*** a new grafport, reset it ***}
 TextFont( GENEVA );
 TextSize( 9 );
 GetFontInfo( FontInfo );
 lineHite := fontInfo.ascent +
 fontInfo.descent +
 fontInfo.leading;

WITH Prec^^.prInfo.rpage DO
BEGIN
lineRect.Top   := top;
lineRect.Bottom:=lineRect.top +lineHite;
lineRect.Right := lineRect.left;
Moveto(lineRect.left,
lineRect.bottom );
END;
tpos := Pointer( ORD( tpos ) +  1 );
END;


PROCEDURE NewLine;
(******************************
* Move to a new position on the
* page.
******************************)
BEGIN
 WITH lineRect DO
 BEGIN
 top  := top + lineHite;
 bottom := bottom + lineHite;
 MoveTo( left, bottom );
 
 IF bottom > pageHite THEN 
 EjectPage;
 
 Right := Prec^^.prInfo.rpage.left;
 END;
END;

PROCEDURE DrawLine;
(******************************
* Draw the current line please
******************************)
BEGIN
 DrawText( cPos, 0, num );
 lineRect.Right := lineRect.left + wordSize;
 tpos := Pointer( ORD( tpos ) + 1 ); { debug }
 num := INTEGER( ORD(tPos) - ORD(wPos) );
 cPos := wPos;
 wPos := tPos;
END;

 
{---- Reporter ----}
BEGIN
 TextFont( GENEVA );
 TextSize( 9 );
 GetFontInfo( FontInfo );
 lineHite := fontInfo.ascent + 
 fontInfo.descent + 
 fontInfo.leading;

{*** get information about page ***}
 WITH Prec^^.prInfo.rpage DO
 BEGIN
 pageHite := bottom - top;
 pageWidth  := right-left;
 lineRect.top  := top;
 lineRect.bottom:= top + lineHite;
 lineRect.left   := left;
 lineRect.right  := left;
 MoveTo( lineRect.left, lineRect.bottom );
 END;

 Hlock( hand );
 cPos := hand^;
 wPos := cPos;
 num  := 0;
 done := false;
 
 REPEAT
 tPos := wPos;
 
 CASE tPos^ OF
 LINEFEED, CR:
 BEGIN
 DrawLine;
 NewLine;
 wpos := tpos;
 END;
 
 FF:
 BEGIN
 EjectPage;
 wpos := tpos;
 lineRect.right := lineRect.right + wordSize;
 END;
 
 NILCHAR:
 BEGIN
 DrawLine;
 done := TRUE;
 END;
 
 OTHERWISE
 BEGIN
 wordSize := CalcNextWord( tPos );
 IF( wordSize + lineRect.right ) < pageWidth THEN
 BEGIN
 num := num + INTEGER( ORD(tPos) - ORD(wPos) );
 wPos := tPos;
 END
 ELSE
 BEGIN
 DrawLine;
 NewLine;
 END;
 lineRect.right:=lineRect.right + wordSize;
 END;
 
 END {** CASE ***};
 UNTIL done;
 
 PRClosePage( printer );
 HUnlock( hand );
END;



PROCEDURE Reporter(pPtr: XCmdPtr);
(*********************
* Print the data that’s 
* passed in as a 
* parameter.
**********************)
VAR
 fieldPtr : Ptr;
 fieldName: Str255;
 fieldType: BOOLEAN;
 fieldData: Handle;
 prRecHandle: THPrint;
 prPort : TPPrPort;
 myStRec: TPrStatus;
 
{$I XCmdGlue.inc }

BEGIN
WITH pPtr^ DO
IF (paramCount <> 0) AND
(params[1] <> NIL) THEN 
BEGIN
 
PROpen;
prRecHandle := THPrint(NewHandle( SIZEOF(TPRINT) ));
PrintDefault( prRecHandle );
 
IF PRJobDialog( prRecHandle ) THEN BEGIN
prPort := PROpenDoc( prRecHandle, NIL, NIL );
PrOpenPage( prPort, NIL );
 
PrintHandle( params[1], prPort, prRecHandle );
 
PrCloseDoc( prPort );
 
 IF(prRecHandle^^.prJob.bJDocLoop = bSpoolLoop) AND (PrError = noErr) 
THEN
 PRPicFile( prRecHandle, NIL, NIL, NIL, myStRec );

END; {*** IF PRJobDialog ***}
 
 DisposHandle(Handle(prRecHandle) );
 PrClose;
 
END; {*** paramCount <> 0 ***}
pPtr^.returnValue := NIL;
END; {*** PROCEDURE Reporter ***}

END.

Listing 1. Reporter.P - A text printing XCMD.

end HyperChat
 
AAPL
$99.76
Apple Inc.
+2.09
MSFT
$44.08
Microsoft Corpora
+0.45
GOOG
$520.84
Google Inc.
+9.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more
Autopano Giga 3.7 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
MenuMeters 1.8 - CPU, memory, disk, and...
MenuMeters is a set of CPU, memory, disk, and network monitoring tools for Mac OS X. Although there are numerous other programs which do the same thing, none had quite the feature set I was looking... Read more
Coda 2.5 - One-window Web development su...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few... Read more
Arq 4.6.1 - Online backup to Google Driv...
Arq is super-easy online backup for the Mac. Back up to your own Google Drive storage (15GB free storage), your own Amazon Glacier ($.01/GB per month storage) or S3, or any SFTP server. Arq backs up... Read more
Airfoil 4.8.10 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more

Latest Forum Discussions

See All

This Week at 148Apps: October 13-17, 201...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »
GAMEVIL Announces the Upcoming Launch of...
GAMEVIL Announces the Upcoming Launch of Mark of the Dragon Posted by Jessica Fisher on October 20th, 2014 [ permalink ] Mark of the Dragon, by GAMEVIL, put | Read more »
Interview With the Angry Birds Transform...
Angry Birds Transformers recently transformed and rolled out worldwide. This run-and-gun title is a hit with young Transformers fans, but the ample references to classic Transformers fandom has also earned it a place in the hearts of long-time... | Read more »
Find Free Food on Campus with Ypay
Find Free Food on Campus with Ypay Posted by Jessica Fisher on October 20th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Strung Along Review
Strung Along Review By Jordan Minor on October 20th, 2014 Our Rating: :: GOT NO STRINGSUniversal App - Designed for iPhone and iPad A cool gimmick and a great art style keep Strung Along from completely falling apart.   | Read more »
P2P file transferring app Send Anywhere...
File sharing services like Dropbox have security issues. Email attachments can be problematic when it comes to sharing large files. USB dongles don’t fit into your phone. Send Anywhere, a peer-to-peer file transferring application, solves all of... | Read more »
Zero Age Review
Zero Age Review By Jordan Minor on October 20th, 2014 Our Rating: :: MORE THAN ZEROiPad Only App - Designed for the iPad With its mind-bending puzzles and spellbinding visuals, Zero Age has it all.   | Read more »
Hay Ewe Review
Hay Ewe Review By Campbell Bird on October 20th, 2014 Our Rating: :: SAVE YOUR SHEEPLEUniversal App - Designed for iPhone and iPad Pave the way for your flock in this line drawing puzzle game from the creators of Worms.   | Read more »
My Very Hungry Caterpillar (Education)
My Very Hungry Caterpillar 1.0.0 Device: iOS Universal Category: Education Price: $3.99, Version: 1.0.0 (iTunes) Description: Care for your very own Very Hungry Caterpillar! My Very Hungry Caterpillar will captivate you as he crawls... | Read more »

Price Scanner via MacPrices.net

2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more
C Spire to offer iPad Air 2 and iPad mini 3,...
C Spire on Friday announced that it will offer iPad Air 2 and iPad mini 3, both with Wi-Fi + Cellular, on its 4G+ LTE network in the coming weeks. C Spire will offer the new iPads with a range of... Read more
Belkin Announces Full Line of Keyboards and C...
Belkin International has unveiled a new lineup of keyboard cases and accessories for Apple’s newest iPads, featuring three QODE keyboards and a collection of thin, lightweight folios for both the... Read more
Verizon offers new iPad Air 2 preorders for $...
Verizon Wireless is accepting preorders for the new iPad Air 2, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB iPad Air 2 WiFi... Read more
Price drops on refurbished Mac minis, now ava...
The Apple Store has dropped prices on Apple Certified Refurbished previous-generation Mac minis, with models now available starting at $419. Apple’s one-year warranty is included with each mini, and... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
Refurbished 2013 MacBook Pros available for u...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available starting at $929. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz MacBook Pros (4GB RAM/... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** As businesses discover the power of Apple computers and mobile devices, it's your job - as a Solutions Engineer - to show them how to introduce these Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.