views:

22

answers:

1

I need a good way to programmatically set the Pages per Sheet setting when printing.

I know that a user can select this option via a printer settings dialog and that I could make such a dialog display if I wanted to. But in the context of the program I'm working on, I need this to be selected by the software itself without requiring to user to delve into the printer settings.

While it appears I can ask Windows if a printer supports this using the dmNup member of the DEVMODE structure, there doesn't seem to be any official way to actually set it for the current print job.

Obviously, one solution would be to alter the printing algorithm in my software so that it lays out the pages appropriately on the device context to get multiple pages per sheet in the resulting output. However, I haven't seen any simple way to do that which doesn't require significant changes throughout all of the code that draws to the printer device context. For various reasons, I would rather avoid massive code changes.

Another solution is to install multiple copies of the printer in Windows while directing them all to the same printer port. Each copy of the printer is set with a different default Pages per Sheet. Then the software can select the appropriate printer and the end result is as if it had programmatically selected the necessary pages per sheet option. However, I have encountered various problems with this scheme, and so I am looking for an alternative that would be less of a support burden.

This article offers another possible solution: manipulating the appropriate byte in the private driver data that follows the official DEVMODE structure. The problem is that there is no guarantee that any particular printer driver actually uses the same byte, and I need to support essentially any printer that could reasonably by used in an office environment. It would seem my software would need a configuration where I could specify different byte offsets for drivers that don't use the default. This could be a rare occurrence or a frequent headache depending how many printer drivers have decided to use different bytes for this setting.

So here are my questions:

  • Does anyone have any real-world experience using the last technique of manipulating private driver data? How well does it hold up when dealing with various printers from various manufacturers?
  • Are there any other techniques that I have overlooked?
  • If nothing else, then are there any simple ways to take code that draws text, lines, graphics, etc to the printer device context and somehow force it to scale, move, and possible rotate (for 2 pages per sheet) the output to the necessary location in the device context without writing all of the code (and without losing 1-pixel wide lines)?

Note: For what it's worth, the program I'm working on is using C++ and MFC. However, all of this should be equally applicable to any program that deals with device contexts and the rest of the Windows API itself.

A: 

Well, its been a while since I've used GDI and MFC, but I did quite a bit of work with printers in the past. I would tend to say that since GDI is supposed to abstract your program from having knowledge about the drawing context that you're out of luck for a specific API call. However, there may be some ways to do it that aren't as hack-ish as slamming the bits in a private area of a structure.

The first thing I would do is look at the CDC::Escape method (or the Escape Win32 API function). In the best case, there is a standard escape sequence to set the printer into n-up mode. In the worst case, you have to find the escape sequence for your printer and send it raw. (Once upon a time, this was the only way to make printer's use their fancy features.) Most printers started to implement a language standard (PCL? HPGL? I forget which one) a while back. It may be that many printers will understand the escape sequence if they still implement standard languages.

If that fails, you'll probably have to manually implement your own n-up drawing on the context but use something like SetMapMode to set the units in Twips (or whatever works) and draw it pre-scaled.

Otherwise, try to see what GDI does with the scaling and see if that works.

Nathan