March 95 - Macintosh Q & A
Macintosh Q & A
Macintosh Developer Technical Support
Q I'm about to write my first QuickDraw GX printer driver (for a laser printer) and
plan to base it on the sample code "Generic LaserWriter." Is there anything I need to
know about that code?
A There are two bugs that you might need to know about, depending on which version of
the sample you're using. Both are very easy to fix.
• In versions previous to those in the QuickDraw GX Developer's Kit
(which is now part of the Mac OS Software Developer's Kit), a device ID of
$88 is specified in the file ChooserSupport.a. For QuickDraw GX, the device ID
should always be $A9. (This bug also slipped through in the
"LaserWriter--custom dialogs" sample in the Developer's Kit.)
• For all the versions of the code in the Developer's Kit, we improved the
file ChooserSupport.c by adding better error handling, making everything
universal, and removing unnecessary code; however, we forgot to add ALRT
-4095 and DITL -4095 to the driver. If the Chooser code fails, it tries to put
up a "You're hosed" alert, which doesn't exist. You should still use this
improved version if you can; you can just copy the missing resources from
any of the other sample LaserWriter drivers in the Developer's Kit.
Q I've just ported my application to the Power Macintosh and I want it to optionally
use the Speech Manager. But if I try to launch the application when the Speech Manager
isn't installed, the Finder puts up a dialog that says "The application `GonzoApp' could
not be opened because `SpeechLib' could not be found." How can I get my application to
launch in this case?
A The Finder refuses to launch your application because the Code Fragment Manager
(CFM) believes your application is "hard linked" to SpeechLib. The CFM assumes your
application can't run at all without SpeechLib and that all references to SpeechLib
must be resolved. Since it can't locate SpeechLib, it refuses to launch your application.
To get around this, the CFM supports a concept called "weak linking." In this case, it
allows references to the library to be unresolved at run time, and it's up to the
application (that is, your code) to check that it's safe to make calls to the library. If
you don't check, your application will crash the first time it tries to call the library.
The best way to check that a weak-linked library is installed is to check that the
address of a function in the library has been resolved. For example:
Boolean HasSpeechLib()
{
/*
* Check the address of some function in the library.
* SpeechManagerVersion is convenient.
* kUnresolvedSymbolAddress is defined in FragLoad.h.
*/
return (SpeechManagerVersion != kUnresolvedSymbolAddress);
}
Note that this is not a replacement for using Gestalt to determine whether services are
available; you should still use Gestalt to determine whether the Speech Manager is
installed. The above code is an additional check for native applications, to ensure that
the necessary library functions are available.
How you weak-link your application to a particular library depends on your
development environment. Using the PPCC tools, you would specify weak linking with
the following option to MakePEF:
-l SpeechLib.xcoff=SpeechLib~
Note the tilde (~) character at the end: this specifies a weak-linked library. In
Metrowerks CodeWarrior, when you add SpeechLib to the project, the little pop-up
menu on the right side of the project window will have an "Import weak" option that
you can use to enable weak linking. Other development environments may use different
methods for designating a weak-linked library.
Q To make my application localizable, I want to use ExtendedToString and
StringToExtended to convert floats to strings and strings to floats. These routines,
though, use the SANE extended format, which is quite archaic. What's the best way to
convert a float to an extended to pass to ExtendedToString? It should compile on both
680x0 and PowerPC machines with MPW, Metrowerks, and THINK C.
A On PowerPC machines, extended80 and extended96 do not exist, except as
structures. There are a number of (PowerPC-only) conversion routines in fp.h, like
x80told. Your formatting code can be written as follows:
#include
#include
void LongDoubleToString (long double ld,
const NumFormatString *myCanonical,
const NumberParts *partsTable,
Str255 outString)
{
#if defined(powerc) || defined (__powerc)
extended80 x;
ldtox80(&ld, &x);
ExtendedToString(&x, myCanonical, partsTable, outString);
#else
#ifdef mc68881
extended80 x;
x96tox80(&ld, &x);
ExtendedToString(&x, myCanonical, partsTable, outString)
#else
ExtendedToString(&ld, myCanonical, partsTable, outString);
#endif
#endif
}
Note that long double is equivalent to extended80 or extended96on 680x0, and
128-bit double-double on PowerPC. If you want to format a double, just pass it in
and the compiler will take care of converting double to long double or double to
extended.
SANE.h is being replaced by fp.h, for both 680x0 and PowerPC. This issue's CD
contains the libraries and interfaces for this new numerics package, which
implements the Floating-Point C Extensions (FPCE) proposed technical draft of the
Numerical C Extensions Group's requirements (NCEG/X3J11.1).
For more information on how to convert a SANE program to PowerPC, see Inside
Macintosh: PowerPC Numerics, Appendix A. In principle, you should replace all use of
extended with double_t. The double_t type maps back to long double for
680x0, which is the same as extended; for PowerPC, it maps to 64-bit double,
which is the fastest floating-point format. Only when you read or write 80- or 96-bit
SANE numbers to files, or when you want to use any of the Toolbox routines that take
an 80-bit extended parameter, do you need to use conversion routines.
QI'd like to automatically configure printing in portrait or landscape mode without
requiring the user to go through the Page Setup dialog. How do I go about doing this?
A The traditional Macintosh printing architecture provides no support for changing
the page orientation programmatically. At the time the Printing Manager was designed
(about ten years ago!), there was kind of an overreaction regarding the principle of
the user being in control, so we've had to live without a dependable way of changing a
print record without the user's help. The good news is the QuickDraw GX printing
architecture does support changing print dialogs programmatically, and QuickDraw GX
will eventually be replacing the old printing architecture.
If you're interested in finding a workaround in the traditional printing environment,
the only one we can offer is to prepare a couple of print records with the desired
settings ahead of time and include them in your application. Then you can select the
appropriate one based on the high-order byte of the TPrStl.wDev field of a validated
print record. If the current printer driver has a value you don't know about, ask the
user to go through the Page Setup dialog once to set landscape mode. Your application
can then save the print record after it's filled out by PrStlDialog (again, indexed by
the high byte of the wDev field).
The best method for saving the print record is to save it as a resource in your
document's resource fork. Make your resource type something different from those
used by the Printing Manager, to prevent the Printing Manager from getting confused
and grabbing the wrong resource.
Remember, you need to be careful: watch the high byte of the wDev field, and call
PrValidate before passing a print record to PrOpenDoc. Also, take a look at Inside
Macintosh: Imaging With QuickDraw; pages 9-17 and 9-18 provide some valuable
information.
Q I want to create a mask for a picture, such that the mask is 0 wherever the picture's
pixels are pure white, and 1 everywhere else. My first try was to simply use CopyBits
to copy the rectangle enclosing the PICT onto a same-sized rect in a 1-bit-deep
offscreen world. This didn't work, however, as the yellows get transformed to 0, which
is not what I want. I tried various transfer modes with CopyBits (from 0 to 100) to no
avail. The SeedCFill and the CalcCMask routines don't seem to be what I want either,
because it appears that their masks have to be keyed off a certain point or side(s). I
can take the brute force approach and go through the pixels of the PICT one by one,
checking to see if they're white and setting the mask accordingly, but this seems
insane. Is there a good method for doing this?
A The way to do this is to install a custom color search procedure, then call CopyBits to
copy into the 1-bit GWorld and let the search proc determine the color to use. The
search proc would simply look at the color asked for and return white if it's white or
black if it's nonwhite. See "Color Manager" in Inside Macintosh: Advanced Color
Imaging (on this issue's CD and forthcoming in print from Addison-Wesley).
QI heard that you can use LaserWriter 8.1.1 with QuickDraw GX with some patch. Is
this true? If so, how?
A You can install an option called QuickDraw GX Helper. To do this, launch the
installer document for QuickDraw GX and choose Custom Install from the pop-up menu
in the top left of the window. A list appears with a bunch of options, with checkboxes
next to them. Click the small triangle next to the QuickDraw GX Utilities option; then
check the QuickDraw GX Helper option below that.
After you install and reboot, Helper will be available to you. It works only with
old-style "classic" printing applications (though there's nothing classic about the old
Printing Manager :-); if you're using a QuickDraw GX-savvy application, Helper
won't help. When you're in a "classic" printing application, there's a new option, Turn
Desktop Printing On (or Off) in the Apple menu.
Be aware that this is more than a patch. Literally, it's an unpatch! It removes some of
the QuickDraw GX patches and is really a bit of a hack, provided for convenience.
Because of this, there isn't much of an interface. For instance, when desktop printing
is turned off, the printer used is the one selected the last time that "classic" driver
was used. To change to a different printer, you need to reboot without QuickDraw GX,
choose a new printer in the Chooser, then reboot again with QuickDraw GX.
Here are some additional things to note:
• Helper will choose LaserWriter 7.x over LaserWriter 8 (because
LaserWriter 7.x was called "LaserWriter," whereas LaserWriter 8.x is called
"LaserWriter 8," and the former is ordinally before the latter). As you
probably know, QuickDraw GX can't tolerate multiple printer drivers with the
same creator codes, so it just picks the first one. Therefore, you should ensure
that you have only one driver of each type in your Extensions folder.
• Of course, before turning off desktop printing you need to ensure that you
have the appropriate desktop printer selected: if you want LaserWriter 8.1.1
selected when you turn off desktop printing, make sure a LaserWriter is the
selected desktop printer; if you want ImageWriter to be selected when you
turn off desktop printing, make sure an ImageWriter is the selected desktop
printer; and so on.
• Helper works only with Apple drivers. If you need to print with another
driver, rebooting without QuickDraw GX is the only option.
Q I'm trying to get a gxFont ID based on a given font name (for example, "Times"), and
I've run across a confusing situation using GXFindFonts. Below is the call I'm using,