March 96 - Print Hints: QuickDraw GX Breaks the Space Hack
Print Hints: QuickDraw GX Breaks
the Space Hack
Dave Polaschek
Before QuickDraw GX, when an application that generated its own
PostScript(TM) code wanted to make sure the printer could print a
particular font, it could send one space character in the needed font. The
LaserWriter driver would check the printer to see if the font was available,
and if not, the driver would send the font to the printer so that it would be
available to print the space character -- and any other characters in that font
that the application-generated PostScript code might require. The reason for
using a space was simple: you didn't want to mark the page just to get a font
to the printer, and a space wouldn't mark it. This technique, first described
in "The Perils of PostScript" back in develop Issue 1, became known as the
"space hack."
Unfortunately, the space hack doesn't work with QuickDraw GX. This column
describes a new way for applications that generate their own PostScript code to
send fonts to the printer. The code to do this is provided on this issue's CD.
QUICKDRAW GX CHANGES THE PICTURE
QuickDraw GX has a really cool imaging model, supports all kinds of whizzy
features, and to top it off, introduces the long-awaited new printing
architecture. But it has one snag: after all the years you've spent getting
your PostScript printing tuned just the way you like it, QuickDraw GX breaks
the space hack.
The space hack depends on a font's entire character set being sent to the
printer in response to the need for a single character (the space character).
But QuickDraw GX sends only the needed characters in a font to a printer,
because it's trying to conserve memory on the printer and also because sending
less data means faster transmission of that data. This isn't such a big issue
with Roman fonts, where there are only 256 characters at most, but in the case
of two-byte fonts such as Chinese, Japanese, and Korean fonts, where there can
be tens of thousands of characters and the font can be tens of megabytes in
size, sending only the required characters makes a big difference in speed.
Incidentally, with QuickDraw GX you don't need a specialized printer to print
two-byte fonts. It divides fonts with more than 256 characters into several
smaller fonts with new encodings containing just the characters you need, so
you can print characters from the font on any PostScript printer.
THE NEW WAY TO DOWNLOAD FONTS
So QuickDraw GX has lots of advantages over QuickDraw, but the space hack is
broken. What's the poor programmer to do?
You can use a new font downloading method based on calling GXFlattenFont, a
handy function introduced with QuickDraw GX, to convert the font to a form
that's easily sent to the printer. GXFlattenFont is intended to convert any
font present on your Macintosh into the output font format of your choice.
(Conversion is limited by the capabilities of the scalers present, as explained
in "QuickDraw GX Font Scalers.")
QUICKDRAW GX FONT SCALERS
The QuickDraw GX Open Font Architecture accepts drop-in font scalers. A font
scaler is a bit of code that takes a font of a given type and converts it to
bitmaps for display. It also converts fonts to outline format and can
optionally convert a font to another font format. QuickDraw GX includes three
default scalers:
- the bitmap scaler, which is essentially the same as in QuickDraw
- the TrueType GX scaler, which supports the TrueType GX format
- the Type 1 scaler, which is part of Adobe(TM) Type Manager
All of these default scalers are capable of generating bitmaps for screen
display and PostScript fonts for printing. Only the TrueType GX scaler can
generate downloadable TrueType fonts.
GXFlattenFont can produce Type 1 data that's ready to be sent to your
PostScript printer with no problem.
Now let's turn to the code that replaces the old space hack. The rough idea is
to call GXFlattenFont on a QuickDraw font reference and a set of characters
(an encoding) that you need to print, and return the result in a form that's
easy to send to the printer. For simplicity, if no encoding is present, we use
the standard Macintosh encoding. Listing 1 shows a font-downloading routine,
FontToPict, that uses this technique if QuickDraw GX is installed. (This is a
somewhat simplified version; see the CD for the full code of FontToPict and its
related utility functions.)
Listing 1. FontToPict
PicHandle FontToPict(short qdFont, short qdStyle)
{
Rect theRect = {0, 0, 1, 1};
PicHandle thePict = OpenPicture(&theRect);
const short kPostScriptHandle = 192;
// If QuickDraw GX is installed, use the new method.
if (GXInstalled()) {
Handle piccommentHdl;
unsigned short *myEncoding = nil;
MakePSHandle(qdFont, qdStyle, myEncoding, &piccommentHdl);
PicComment(kPostScriptHandle, GetHandleSize(piccommentHdl),
piccommentHdl);
} else {
// If QuickDraw GX isn't installed, use the old method.
Point penPoint;
// We would normally set the clip here, but since we're just
// drawing a space there's no need.
GetPen(&penPoint); // Save the pen location.
TextFont(qdFont);
TextFace(qdStyle);
DrawChar(' ');
MoveTo(penPoint.h, penPoint.v); // Restore the pen location.
}
ClosePicture();
return (thePict);
)
FontToPict
starts by checking to see if QuickDraw GX is installed. If not, it uses the old
hack of printing a space; otherwise, it calls MakePSHandle (Listing 2), which
calls the utility function ConvertQDFontToGXFont
to convert the QuickDraw font reference into a QuickDraw GX font reference.
MakePSHandle then checks to see if an encoding has been passed in; if not, it
builds the standard Macintosh encoding. Next it
calls FontToHandle, which is just a wrapper for GXFlattenFont. GXFlattenFont
converts the specified font to the Type 1 format. Error-handling and cleanup
code is last. Simplicity itself! The result, whether QuickDraw GX is present or
not, is a PICT that you can send to the printer by calling DrawPicture once the
printer port has been opened.
When calling MakePSHandle, you should specify an encoding array that contains
the characters you intend to actually print. This prevents QuickDraw GX from
sending the entire font to the printer and becomes very important when you make
your application WorldScript aware. There's an #ifdef in the code on the CD
that generates only the encoding array you need in order to use a portion of
the font. As mentioned earlier, with Chinese, Japanese, and Korean fonts,
sending only the characters you need can make the difference between sending a
few kilobytes or many megabytes of data to the printer. If you don't use the
entire font, remember to encode the characters that you want to draw,
using the same encoding that you passed in to the MakePSHandle function.
Listing 2. MakePSHandle
OSErr MakePSHandle(short qdFont, char qdStyle,
unsigned short *encodingArray, Handle *outputHandle)
{
OSErr status = noErr;
gxFont theFont;
unsigned short *myEncoding;
Boolean madeEncoding = false;
// Convert to a QuickDraw GX font reference.
theFont = ConvertQDFontToGXFont(qdFont, qdStyle);
// If no encoding, create the standard Macintosh encoding.
if (!encodingArray) {
long returnLength;
myEncoding =
(unsigned short *)NewPtrClear(256 * sizeof(short));
returnLength = MakeMac8BitEncoding(theFont, myEncoding);
if (returnLength != 256) {
DebugStr("\pHmm. We didn't get a full encoding.");
return (returnLength); // Pass the error along.
}
madeEncoding = true;
} else {
myEncoding = encodingArray;
}
*outputHandle = FontToHandle(theFont, myEncoding);
if (madeEncoding) DisposePtr((Ptr)myEncoding);
status = MemError();
if (status == noErr) {
status = GXGetGraphicsError(nil);
if (status != noErr) {
DisposeHandle(*outputHandle);
*outputHandle = nil;
}
}
return (status);
}
You
may want to have HandleSpoolProc (which is called by GXFlattenFont and included
on the CD) spool directly to the printer via picture comments. This way you
won't need memory available to hold the font data at the intermediate steps.
DOWNLOADING HAPPINESS
The new font downloading method takes a little more work but produces better
results in your printer font handling. You can easily send needed fonts to the
printer, either the whole font or only the characters you'll be using. As a
side benefit, you get support for two-byte font systems without having to write
custom
code for handling the large fonts or, worse yet, having to depend on the fonts
being installed on the printer in a specific manner. Even if you're not ready
to add QuickDraw GX imaging to your application today, adding QuickDraw GX
compatibility improves the printing experience for your customers.
DAVE POLASCHEK recently relocated to California to join Apple's Developer
Technical Support group. He's been told that supporting printing leads to hair
loss and insanity. Dave previously lived in beautiful sunny Minnesota, and
wonders if he'll get used to the harsh San Francisco Bay Area winters before
he's bald and crazy, or if it's already too late.*
Thanks to Dan Lipton for providing the idea and core code illustrating the new
font downloading method, and to Pete "Luke" Alexander, Dave Hersey, and Dan
Lipton for reviewing this column.*