RFR: 8315113: Print request Chromaticity.MONOCHROME attribute does not work on macOS

Phil Race prr at openjdk.org
Mon Apr 8 23:22:00 UTC 2024


On Mon, 11 Mar 2024 13:54:02 GMT, Alexander Scherbatiy <alexsch at openjdk.org> wrote:

> The fix provides ability to print Black & White pages on macOS.
> 
> Cocoa API has [PMSetColorMode](https://developer.apple.com/documentation/applicationservices/core_printing/1805783-pmsetcolormode) function but it is marked as deprecated and really does nothing.
> 
> There is no replacement; this function was included to facilitate porting legacy applications to macOS, 
> but it serves no useful purpose.
> 
> Dumping `NSPrintInfo` print settings which were set by the native print dialog on macOS shows that the keys and values used for Black & White printing depend on the used printer type.
> For example, the tested 
> `HP Color LaserJet MFP M180n` printer uses `ColorModel` key and`Gray` value, and
> `HP Ink Tank 110 series` uses `HPColorMode` key and `grayscale` value.
> 
> Printing all `NSPrintInfo` presets shows that they do not contain key/value pairs  for Black&White settings.
> This is the code snippet used to print `NSPrintInfo` presets:
> 
>     PMPrinter pr;
>     PMPrintSession printSession = (PMPrintSession)[printInfo PMPrintSession];
>     OSStatus status = PMSessionGetCurrentPrinter(printSession, &pr);
>     CFArrayRef presetsList = nil;
>     status = PMPrinterCopyPresets(pr, &presetsList);
>     CFIndex arrayCount = CFArrayGetCount(presetsList);
> 
>     for (CFIndex index = 0; index < arrayCount; index++) {
>         PMPreset preset = (PMPreset)CFArrayGetValueAtIndex(presetsList, index);
>         CFStringRef presetName = nil;
>         if (PMPresetCopyName(preset, &presetName) == noErr && CFStringGetLength(presetName) > 0) {
>             NSLog(@"  presetName: '%@'", presetName);
>             NSDictionary* dict = nil;
>             if (PMPresetGetAttributes(preset, (CFDictionaryRef*)(&dict)) == noErr) {
>                    // print preset dict
> 
> 
> The idea of the proposed fix is to store printer dependent key/value pairs in the `<jdk-home>/conf/printer.properties` property file.
> 
> The property file has the format:
> 
> print-attribute.print-attribute-value.key=value
> 
> where `print-attribute` is the java print attribute, `print-attribute-value` is the corresponding attribute value, and `key` and `value` is the key/value pair used by a specific printer.
> 
> For example, for `Chromaticity` attribute the property file could look like:
> 
> Chromaticity.MONOCHROME.ColorModel=Gray
> Chromaticity.COLOR.ColorModel=CMYK
> Chromaticity.MONOCHROME.HPColorMode=grayscale
> Chromaticity.COLOR.HPColorMode=color
> 
> where `Chromaticity.MONOCHROME` key prefix corresponds to `Chromaticity.MONOCHOROME` print attribute constant  with  (...

Since the user can always select greyscale in the user dialog, I presume you have some requirement
to do this printing without user intervention. Perhaps there's no user there (server-side printing), or
the app would like to make sure the user always defaults to B&W.

As far as I can tell, this isn't supported via any kind of API on macOS, so it is not a Java problem.
You could be printing from an Objective-C app written directly to Cocoa APIs and still have the same problem.
So a Java-specific workaround like this seems inappropriate.

I've poked around to help me understand what is going on.

When I do "Print to File" on macOS - meaning use the native printer dialog's "Save as PDF" option,
then the generated PDF is always colour even if I select the printer dialog's "Grayscale Printing" option.
This isn't just true for Java apps, the same happens if I print a web page from Safari.

And when I query the  Chromaticity support for a couple of colour printers - one Canon, one HP,
both report they support it but only the value COLOR.

So I conclude the grayscale printing option is something handled by the printer driver at the time it is converted to
the printer-specific format and sent to the physical printer and it isn't changing the rendering.

So in general without an API, to get greyscale it requires the end-user to set an option in a print dialog
and as mentioned above, what you are trying  o enable isn't possible even if you wrote the app in
Objective-C or Swift directly as a macOS native app.

But what works for me to get greyscale by default is to set that up as the default for the print queue.
It is easy to add a printer twice - with two queues for it.
One has the default set to greyscale, the other to colour. Then the app just needs to select the required queue.
This is clearly more site-specific than having the app just specify MONOCHROME but is a lot lower cost all round.

I don't understand why PMSetColorMode is deprecated and non-functional since it seems like it would
be the right option here, but then internally macOS would need to be able to map it to the right option.
Doubtless that would be easier if MONOCHROME were supported by all these drivers and I don't understand
that omission either. In fact if it were we probably could just specify that directly without macOS needing
to understand anything - like  I expect it doesn't understand the key/value pairs your code is sending it.
I note that you send ALL known key/value pairs and hope one works .. and that points out that all
it takes is some vendor API change or a different printer and it just won't work again.
These points and the implied maintenance cost are some of my bigger concerns with this.

Perhaps you should engage with Apple and get them to properly support MONOCHROME.
Or maybe it would be enough if the printer vendors do ?
FWIW, I don't think it is an Apple-specific issue, the free drivers for Linux behave similarly.
Perhaps there's a clue in there somewhere. But similarly you can set up a separate queue.

The other idea that crossed my mind is that instead of relying on a printing solution, we could
explicitly render in greyscale. Then we could always declare MONOCHROME support and just emulate it.
But I am not sure if this is really possible - it would require changes to the rendering code and
I'm not sure if or how if we'd be able to do that properly and safely. And the risks of that clearly extend beyond printing.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/18195#issuecomment-2043813832


More information about the build-dev mailing list