Print Incompatible
Volume Number: 6
Issue Number: 6
Column Tag: Resource Roundup
20 Steps to Printing Incompatibility 
By Joel West, Palomar Software, Oceanside, CA
20 Steps to Printing Incompatibility
Incompatible printing code. Based on what I’ve seen, this a popular pastime
among Macintosh software developers.
First, some background. I’d like to apologize to MacTutor readers for the long
delay between articles. Things were pretty busy in 1988 as we worked on our printer
drivers, culminating in the January announcement at Macworld Expo of drivers we
developed for the HP PaintJet and the Howtek PixelMaster.
For an application writer, the two drivers represent two more examples of
output devices to be compatible with. The PaintJet 1.0 supports 2, 8, and 256 (fixed)
colors with bitmap fonts, while the PixelMaster 1.0 is an 8-color and 24-bit color
driver with the Mirus/URW outline fonts.
There are many other Chooser-level printer drivers being offered out there. If
you count just those drivers that are sold with a printer (see Table 1), the number
has exploded from two years ago, when there were two Apple printers and no
third-party solutions. There are many more (I’ve never been able to keep up)
independent drivers, from companies like Phoenix (née Softstyle) and GDT.
Of course, it’s not possible for every application to test with every printer
driver for incompatibilities, which brings me to one of the main points of this article.
Device Independence
Many programmers think you have to know what printer you’re sending to. Each
printer has its own characteristics, the argument goes, and it’s important to optimize
for those characteristics.
Nine times out of ten, that’s plain wrong. The Macintosh printing architecture is
designed to be device-independent, and, though it has its weaknesses, in that regard it
is pretty successful.
I’m certainly not part of a Thought Police that forbids you from such practices.
It’s just that one of the largest areas of application/printing incompatibility comes
from device-dependencies.
It’s particularly frustrating because these problems are almost always
unnecessary. Perfectly good driver-independent solutions are available for rotated
text, smoothed polygons, custom line layout, determining paper size, and other
problems described later on.
And there is no question that the problems introduced by such
device-dependencies, on the balance, outweigh the benefits of same.
Read the Documentation
The second easy way to avoid problems is to restudy the documentation, beginning
with the “Printing Manager” chapters of Inside Macintosh. Too many people don’t
even understand these basics, as evidenced by some of the problems below.
A sample printing loop is shown in Example 1. Using such a loop (or something
very similar) will, unfortunately, rule out at least five of the incompatibility
opportunities below.
When doing your basic re search, don’t forget that the rules have changed over the
years.
There’s PrGeneral functionality in Inside Macintosh, Volume V. Among the tech
notes, I’d recommend #92, 118, 125, 149, 161 and 192 for compatibility, and
#72, 73 ,91 and 95 for extra functionality.
There are many applications that want to get the printing code to do something
that is not in the published interfaces. I understand this problem well and am
certainly sympathetic; perhaps three years ago I got into this debate on Usenet,
arguing the plight of the application developer. One of the examples I used was for a
painting program to set the Imagewriter (or other drivers) to “square pixels” or the
equivalent.
Unfortunately, if you’re not following the documented interfaces, how can you
pull it off? Usually the answer is trial and error iteration. It may work with the two
drivers you try, but there will be 10 or 20 more that do don’t try, and many of these
will break.
Today, one of the better solutions is to provide preformatted stationery with
configured print records for specific printer drivers. Another approach would be to
allow the user to set a default print record once, and then use that print record from
then on out.
If you’re not sure your approach is compatible, I’m sure ZZ or Luke at tech
support would be glad to offer advice on a specific trick or approach. As always, link
to MacDTS.
20 Easy Steps
However, you may decide that you really want printer incompatibilities.
Perhaps you’re trying to sell a particular brand of printer. Or maybe you need a
crisis to convince management to hire a large tech support staff.
Or maybe if your sales are poor enough, your publisher will go bankrupt and you
can reclaim your copyright to market a better version of your application.
Based on Palomar’s re search, and with assistance from other driver writers,
we’ve found that such incompatibility is very easy for you to achieve.
To make things more complete, I’ve decided to mention specific examples where I
had them. This is not to reward these particular programmers or companies, but just
note the ones that happen to be on my list.
To my knowledge, all of these problems are known to the application developer
and most are either fixed in later releases, or in the process of being fixed.
The list is heavily skewed to the major applications, because those are the ones
Palomar tests with most extensively. This doesn’t mean that these applications are
more successful in obtaining incompatibility; nay, we expect that there are many more
unheralded applications on the second 50 of a top 100 list that are equally worthy.
Finally, in defense of my fellow programmers, it should be noted that the
Macintosh printer architecture has evolved in a somewhat ad hoc fashion since the
Imagewriter was introduced in 1984. As such, most of the decisions listed below
reflect reasonable guesses based on an incomplete specification; many would qualify as
outdated assumptions more than deliberate perversions.
With that, let’s get on to the list of easy incompatibilities.
1. Assume only two printers
Example: FullPaint
To my knowledge, only one application checks the wDev field and puts up the alert
“could not find the print driver.” This unique code looks something like this:
/* 1 */
if (wDev>>8 == bDevLaser)
{ /* print one way */
}
else if (wDev>>8 == bDevCItoh)
{ /* print another */
}
else
{ Alert(ALRT_YouLoseSucka, NULL);
}
However, many (usually older) applications have code that looks like:
if (wDev>>8 == bDevLaser)
{ /* do nice printing */
}
else
{ /* do cruddy 144 dpi printing */
}
There have been more than 20 different printer drivers allocated a distinct wDev
value. Just because it isn’t a LaserWriter, doesn’t mean that it will produce cruddy
output.
2. Special case on printers
Example: Digital Darkroom 1.0, XPress 2.0
This is slightly more subtle than the previous approach, and offers many
opportunities for incompatibilities:
1. If new printers are introduced, you get lousy results with them.
2. If existing drivers are improved, you get the same results as with the old driver.
3. If the implementation of the driver changes, you will likely break if you use
device-specific information (such as the layout of the print record).
There is one common special casing that has produced less incompatibilities than
most:
/* 2 */
if (wDevv>>8 == bDevLaser)
{ /* send PostScript stuff */
}
else
{ /* send QuickDraw stuff */
}
However, this will cause problems with a save-now, print-later utility like
Glue or OpenIt.
Many, many applications go well beyond this PostScript/non-PostScript
distinction to achieve printing incompatibility. They use all sorts of intimate details
about the driver, what comments its supports, etc., etc. In several leading
applications, the author has gone to the trouble of building tables with device-specific
information so all sorts of things will be handled differently.
As will be examined below, in most cases there are other ways to accomplish the
same thing. For example, you can special case the paper size based on the wDev
(incompatible) or read the prInfo.rPage and rPaper fields of the print record
(compatible).
For many applications, this is the preferred approach to printing
incompatibility. I’ve picked Digital Darkroom because the incompatible and
compatible approaches are about the same difficulty.
In particular, DD has a better halftoning mechanism that it only makes available
to the LaserWriter, LaserWriter SC, and GCC PLP. This means that any other
high-resolution black & white printer is out of luck.
One better approach would be to always make this advanced halftone available. A
second, more restrictive approach, would be to pick a threshold resolution, (say 220
or 300 dpi), and if a PrGeneral()/getRsl call returns the maximum or device
resolution as higher than that, then make the option available.
3. Bypass printer dialogs
Examples: HyperCard 1.2, MPW 3.0
This is the last of the items that every printer driver writer mentions.
It is the bane of our existence, because every time we add functionality to a
driver, the application takes it away (or makes it difficult to find). Many printers
have a print quality selection, or paper feed option; some have a spool for later
printing options; our drivers include a preview before printing options. Try
addressing a fax without the fax dialog some time.
HyperCard and MPW are two applications that do not show the job dialog for the
“Print” command.
I should also note that Word 3.0 makes it a challenge to get at the dialogs
(something that is fixed in 4.0.) PageMaker 3.0 has a non-standard interface that
only gives you the style (“Page Setup”) dialog after you select printing.
From a personal standpoint, I find the hierarchical menus in Cricket Presents
2.0 to be somewhat frustrating and confusing. On the other hand, I’ve long bitched that
MORE 1.0 should print bullet charts in landscape and outline views of the same
document in portrait, so I guess Cricket has a creative (if unusual) way to provide
needed functionality.
4. Spool-a-page, print-a-page
Example: MacWrite 5.0, XPress 2.0
Once upon a time, people tried to print large files from floppy-based systems.
Thus, some applications would submit print jobs to the printer one page at a time.
This is no longer necessary, but it does achieve incompatibility by defeating the
functionality of many printer drivers. As noted in Macintosh Technical Note #125
this is a good way, for example, to confuse print spoolers.
The counter approach is for the driver to lie and always claim it is “draft”
printing. Unfortunately, this reduces the performance of more normal applications,
which may not be expecting to free much memory for draft printing.
5. Directly change GrafPort fields
Example: SuperPaint 1.0, FullWrite 1.0
There are several reasons why this is a great way to achieve incompatibility just
on general principles.
But we found that when you supply a CGrafPort for color printing to an
application, there are a few “bit bashers” that provide frequent introduction to “la
bomba.”
For example, if you directly change grafptr>bkPat (as does SuperPaint 1.0)
then, on a CGrafPort, you are clobbering the cgrafptr>bkPixPat handle. If the address
is weird enough, like 0xFFFFFFFF (can you say “black”), you will get a bus error.
Those GrafPort fields that are used for other fields in a CGrafPort include:
portBits
bkPat
fillPat
pnPat
Change any of these directly and you’re almost certain to achieve that desired
level of incompatibility.
The bottom line to achieving incompatibility is to make sure your field
manipulation will be invalid on either a GrafPort, Color QuickDraw CGrafPort , or a
32-Bit QuickDraw CGrafPort. The 6.0 LaserWriter driver is one way to test this, as
are various drivers from Tek, Palomar and the film recorder companies.
6. Send bitmaps instead of text
Example: CricketDraw 1.1, FreeHand 1.0, HyperCard 1.2
This one actually is more work than working compatibly. After all, the Font
Manager knows how to draw text, if you just use DrawText() and DrawString()
instead of CopyBits().
Also, no one is going to do this on the LaserWriter - every customer will notice it
immediately - so you must special case out this approach on the LaserWriter. Thus,
it’s even more work to do CopyBits() for just the other printers.
My favorite example is the Cricket Expressions drivers with built-in URW
outline fonts. Because CricketDraw sends only bitmaps, those fonts are not available
and you get those lovely low-res bitmaps.
This one is a real opportunity to put your worst foot forward. Can you imagine
how lousy 72 dpi text looks on a 4,000-line film recorder or 300 dpi printer? Can
you imagine any serious business professional using your application on such an
output device? Your place in incompatibility heaven is assured.
7. Send bitmaps instead of rotated text
Example: MacDraw II 1.0
In this case, it’s easier to be incompatible than compatible, but it’s not strictly
necessary.
The original designers of MacDraw (1.9) came up with a clever way of hiding
both the bitmap and text representations of rotated text. This means that a smart
printer driver (e.g., the LaserWriter) can get at the actual text and its angle, while a
dumb printer driver (e.g., ImageWriter 2.7) will automatically print the bitmap
representation.
The compatible way is to use the picTextBegin and picTextEnd comments, as
documented by Macintosh Technical Note #91.
How does this work? You send both the bitmap and text representations, using an
empty clipping rectangle to “hide” the text from any dumb printer drivers, as below:
/* 3 */
PicComment(picTextBegin, begh);
PicComment(picTextCenter, ctrh);
GetClip(saveclip);
ClipRect(gZeroRect);
DrawString (theText);
SetClip(saveclip);
CopyBits(theBits);
PicComment (picTextBegin, NULL)
MacDraw 1.9.5 did it this way, but the MacDraw II assumed that any printer
that’s not a LaserWriter is brain-dead. However, the good folks at Claris don’t seem to
appreciate incompatibilities, so it’s back to the compatible approach with MacDraw II
1.1.