On Mon, 8 Apr 2024 23:19:13 GMT, Phil Race <p...@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.MO...
>
> 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 ...

> @prrace thanks for the detailed thoughts on this problem. As the stakeholder 
> in this issue, I wanted to offer some feedback to your thoughts. I understand 
> that the desire for OpenJDK is to NOT maintain a list of proprietary printer 
> settings, but since many of the thoughts above get into use-case scenarios, I 
> feel compelled to share my perspective. I hope these are well received.
> 
> > 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.
> 
> Several PDF printers (on several OSs) ignore grayscale/b&w settings. I'm not 
> sure why, but I've observed the same.
> 
> > 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.
> 
> Agreed.
> 
> > So a Java-specific workaround like this seems inappropriate.
> 
> As the stakeholder (biased) it's hard for me to agree, however allowing the 
> implementing application to maintain (and thus offer) this workaround would 
> certainly suffice. Since AWT (quite graciously) abstracts these attributes 
> away from the user, "wontfix" status naturally causes a feature gap, albeit 
> not the fault of OpenJDK, but a parity that -- at a bare minimum -- could 
> live on as -- for example -- a workaround on Stackoverflow, etc.
> 
> > 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.
> 
> Easy is subjective (and prescriptive). Again, biased, but setting up 
> additional printer queues is a viable workaround, but comes at a factor of 2 
> queues for each color printer added to a system. This also offers support 
> redundancies when removing and re-adding a printer (e.g. IP, port or driver 
> change).
> 
> > 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.
> 
> Agreed.
> 
> > [...] 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.
> 
> Understood.
> 
> > 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.
> 
> Since printers can emulate grayscale using color cartridges, I do not believe 
> this will yield the same results as expected on all printers, but I do 
> believe this to be a creative stop-gap/workaround which may be suitable for 
> some use-cases. Naturally, I would expect some performance implications as a 
> result. As the stakeholder, I can say that this will be trickier with prints 
> using vector graphics than those already rastered/pixels. Speculating, vector 
> graphics will likely be less performance-affecting, but require diving into 
> the document modifications (e.g. PDFBOX, JavaFX) albeit more performant, will 
> require some creative code to desaturate all color data in all objects prior 
> to sending (or requests to add APIs thereo to each specific project).
> 
> > Perhaps there's no user there (server-side printing), or the app would like 
> > to make sure the user always defaults to B&W.
> 
> I'm not sure how much interest there is in understanding the use-case that 
> discovered this issue, but it's part of a (pretty popular) Java app that 
> helps print documents. It does so through a WebSocket interface... so in that 
> sense it is server-like (effectively headless), although it's predominantly 
> used by ordinary, non-technical end-users and runs as a sort of background 
> service. Much like AWT offers a (rather) standardized API for printing, so 
> does the Websocket interface, so "Black & White" or "Monochrome" are offered 
> as common, selectable options. "Always default to B&W" would be a 
> case-by-case situation, depending on how the websocket API is offered up 
> (e.g. web app) to the end-user.

Use cases are always interesting.

The idea about changing our rendering to be greyscale would be easier if the 
printing API explicitly supported it.
It could be quite challenging and risky to actually do it otherwise, so it is 
just an idea.

Windows/GDI printing lets you set whether you want color or not with a standard 
field in the struct which controls printing
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodew
SFAIK that isn't using some driver-specific way of rendering and it seems to 
work fine.

Although I've yet to find any evidence of it being available, it is possible 
that printers using IPP on macOS
will support print-color-mode as documented here :  
https://www.pwg.org/ipp/ippguide.html
If that becomes standard and widely available it would be a better way to do 
this.
But I need to understand it better, which would start with finding *anything* 
that supports it.

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

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

Reply via email to