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:

          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.*