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
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Fantastical 2.3.6 - Create calendar even...
Fantastical 2 is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
Creative Kit 1.1 - $149.99
Creative Kit 2016--made exclusively for Mac users--is your ticket to the most amazing images you've ever created. With a variety of powerful tools at your fingertips, you'll not only repair and fine-... Read more
iMazing 2.2.3 - Complete iOS device mana...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
Apple Configurator 2.4 - Configure and d...
Apple Configurator makes it easy to deploy iPad, iPhone, iPod touch, and Apple TV devices in your school or business. Use Apple Configurator to quickly configure large numbers of devices connected to... Read more
WhatRoute 2.0.18 - Geographically trace...
WhatRoute is designed to find the names of all the routers an IP packet passes through on its way from your Mac to a destination host. It also measures the round-trip time from your Mac to the router... Read more
Posterino 3.3.5 - Create posters, collag...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more
Skim 1.4.28 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
Apple macOS Sierra 10.12.4 - The latest...
With Apple macOS Sierra, Siri makes its debut on Mac, with new features designed just for the desktop. Your Mac works with iCloud and your Apple devices in smart new ways, and intelligent... Read more
Apple Numbers 4.1 - Apple's spreads...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
Xcode 8.3 - Integrated development envir...
Xcode includes everything developers need to create great applications for Mac, iPhone, iPad, and Apple Watch. Xcode provides developers a unified workflow for user interface design, coding, testing... Read more

Power Rangers: Legacy Wars beginner...
Rita Repulsa is back, but this time she's invading your mobile phone in Power Rangers: Legacy Wars. What looks to be a straightforward beat 'em up is actually a tough-as-nails multiplayer strategy game that requires some deft tactical maneuvering.... | Read more »
Hearthstone celebrates the upcoming Jour...
Hearthstone gets a new expansion, Journey to Un'Goro, in a little over a week, and they'll be welcoming the Year of the Mammoth, the next season, at the same time. There's a lot to be excited about, so Blizzard is celebrating in kind. Players will... | Read more »
4 smart and stylish puzzle games like Ty...
TypeShift launched a little over a week ago, offering some puzzling new challenges for word nerds equipped with an iOS device. Created by Zach Gage, the mind behind Spelltower, TypeShift boasts, like its predecessor, a sleak design and some very... | Read more »
The best deals on the App Store this wee...
Deals, deals, deals. We're all about a good bargain here on 148Apps, and luckily this was another fine week in App Store discounts. There's a big board game sale happening right now, and a few fine indies are still discounted through the weekend.... | Read more »
The best new games we played this week
It's been quite the week, but now that all of that business is out of the way, it's time to hunker down with some of the excellent games that were released over the past few days. There's a fair few to help you relax in your down time or if you're... | Read more »
Orphan Black: The Game (Games)
Orphan Black: The Game 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Dive into a dark and twisted puzzle-adventure that retells the pivotal events of Orphan Black. | Read more »
The Elder Scrolls: Legends is now availa...
| Read more »
Ticket to Earth beginner's guide: H...
Robot Circus launched Ticket to Earth as part of the App Store's indie games event last week. If you're not quite digging the space operatics Mass Effect: Andromeda is serving up, you'll be pleased to know that there's a surprising alternative on... | Read more »
Leap to victory in Nexx Studios new plat...
You’re always a hop, skip, and a jump away from a fiery death in Temple Jump, a new platformer-cum-endless runner from Nexx Studio. It’s out now on both iOS and Android if you’re an adventurer seeking treasure in a crumbling, pixel-laden temple. | Read more »
Failbetter Games details changes coming...
Sunless Sea, Failbetter Games' dark and gloomy sea explorer, sets sail for the iPad tomorrow. Ahead of the game's launch, Failbetter took to Twitter to discuss what will be different in the mobile version of the game. Many of the changes make... | Read more »

Price Scanner via MacPrices.net

Is A New 10.5-inch iPad Still Coming In April...
There was no sign or mention of a long-rumored and much anticipated 10.5-inch iPad Pro in Apple’s product announcements last week. The exciting iPad news was release of an upgraded iPad Air with a... Read more
T-Mobile’s Premium Device Protection Now Incl...
Good news for T-Mobile customers who love their iPhones and iPads. The “Un-carrier” has become the first national wireless company to give customers AppleCare Services at zero additional cost as part... Read more
FileWave Ensures Support for Latest Apple OS...
FileWave multi-platform device management providers announced support for Apple’s release yesterday of iOS 10.3, macOS Sierra 10.12.4, and tvOS 11.2. FileWave has a history of providing zero-day... Read more
Use Apple’s Education discount to save up to...
Purchase a new Mac or iPad using Apple’s Education Store and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free: -... Read more
Apple refurbished Apple Watches available sta...
Apple is now offering Certified Refurbished Series 1 and Series 2 Apple Watches for 14-16% off MSRP, starting at $229. An Apple one-year warranty is included with each watch. Shipping is free: Series... Read more
9-inch 32GB Space Gray iPad Pro on sale for $...
B&H Photo has the 9.7″ 32GB Space Gray Apple iPad Pro on sale for $549 for a limited time. Shipping is free, and B&H charges NY sales tax only. Their price is $50 off MSRP. Read more
13-inch MacBook Airs on sale for $100-$150 of...
B&H Photo has 13″ MacBook Airs on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 1.6GHz/128GB MacBook Air (MMGF2LL/A): $899 $100 off MSRP - 13″ 1.... Read more
13-inch MacBook Airs, Apple refurbished, in s...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
12-inch Retina MacBooks on sale for $1199, sa...
B&H has 12″ 1.1GHz Retina MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100 off MSRP - 12″ 1.1GHz... Read more
Save up to $260 with Apple refurbished 12-inc...
Apple has Certified Refurbished 2016 12″ Retina MacBooks available for $200-$260 off MSRP. Apple will include a standard one-year warranty with each MacBook, and shipping is free. The following... Read more

Jobs Board

Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
Desktop Analyst - *Apple* Products - Montef...
…technology to improve patient care. JOB RESPONSIBILITIES: Provide day-to-day support for Apple Hardware and Software in the environment based on the team's support Read more
*Apple* Mobile Master - Best Buy (United Sta...
**493168BR** **Job Title:** Apple Mobile Master **Location Number:** 000827-Denton-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** At Read more
Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
*Apple* Mobile Master - Best Buy (United Sta...
**492889BR** **Job Title:** Apple Mobile Master **Location Number:** 000886-Norwalk-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.