The following code snippet should return the Media Trays available to a printer.
However, with some drivers, specifically Ricoh PCL6 Driver for Universal Print
and HP Universal Printing PCL 6
, in addition to Printer Trays, these drivers also list paper types such as Recycled
, Thick
, Matte
, etc.
From what I can tell, OpenJDK is properly using DC_BINNAMES
when calling DeviceCapabilities
. OpenJDK doesn't even seem to use DC_MEDIATYPENAMES
at all in the source code, so I wouldn't expect e.g. Purple Paper
to even be a queryable property, yet it lists when querying trays from the Ricoh driver.
So what's wrong? Are these PCL 6 drivers just bugged? Is DeviceCapabilities
at fault? Or does the bug live in OpenJDK?
import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.standard.Media;
public class TrayListing {
public static void main(String ... args) {
String printerName = "HP LaserJet ..."; // TODO: change this to the actual printer name
PrintService[] allPrinters = PrintServiceLookup.lookupPrintServices(null, null);
for(PrintService ps : allPrinters) {
if(ps.getName().equalsIgnoreCase(printerName)) {
// loop over media trays
System.out.println("\n\nFound MediaTray:");
// Some HP, Ricoh printers/drivers list items that aren't printer trays, such as paper types
for(Media m : (Media[])ps.getSupportedAttributeValues(Media.class, DocFlavor.SERVICE_FORMATTED.PAGEABLE, null)) {
if (m instanceof javax.print.attribute.standard.MediaTray) {
System.out.println("- " m " (" m.getClass().getName() ")");
}
}
}
}
}
}
Additional keywords:
PCL XL Feature Reference
CodePudding user response:
The drivers are bugged. Workarounds exists, but they are complex.
The short:
- Match driver name on
Ricoh|HP
andPCL6|PCL 6
- Filter any trayIds > 1000
The long:
Some drivers such as HP expose the printer trays properly in other areas, for example:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<PRINTER_NAME>\PrinterDriverData
InputSlot
InputSlotDisplayNames
... however this isn't true for drivers such as Ricoh.
After examining a lot of drivers (HP, Ricoh, Xerox, Konica, etc) I've isolated the issue to the following:
- PCL6 drivers
- HP or Ricoh as the vendor
In cases with both vendors, the DC_BINS
value is always > 1000, which is partially explained in the PCL6 MediaSource
specification, quoting:
"... External input trays
1
through248
are selected by substituting the values8
through255
for the enumerated values. Example,8
=first external input tray
,9
=second external input tray
, etc. ..."
Although there's nothing about the 1,000 specifically, and vendors such as Xerox use values over 7000 without the bug. That said, with the problematic vendors, what is observed is that when values are > 1,000 they tend to be actually valid MediaType
values (NOT MediaSource
values), but incremented by 1,000.
Oddly enough, this is very limited to HP and Ricoh and does not apply to other PCL drivers. For example:
- Konica uses trayID of
1000
=LCT
, or "Large Capacity Tray", which is valid. - Xerox offers a PCL6 driver, but commonly uses trayIDs higher than 1000, e.g.
7153
=Tray 1
,7154
=Tray 2
. - Ricoh is known to use trayID of
1025
in the PCL5 version of its driver, which is a valid tray value of1025
=Auto Tray Select
, but this doesn't seem to be the case for their PCL6 driver, which has theMediaType
values mixed in.
So to "fix" this issue, I wrote a series of custom parsing to find out the driver vendor and the tray id.
To locate the trayId from Java:
// Get default printer
PrintService ps = PrintServiceLookup.lookupDefaultPrintService();
for(Media mediaTray : (Media[])ps.getSupportedAttributeValues(Media.class, null, null)) {
// Warning: Reflective operation on Windows-only class
Method method = ps.getClass().getMethod("findTrayID", MediaTray.class);
Object trayId = method.invoke(ps, new Object[]{mediaTray});
System.out.println(trayId);
}
To obtain the driver vendor from Java:
- This part is much more complex and requires a third-party dependency, JNA. For this reason I'm omitting the code, but instead I will provide the steps:
- Reading the registry is as simple as calling
Advapi32Util.registryGetStringValue(...)
- Knowing which registry String value to obtain the driver name is a complex operation, but the following areas are helpful:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Printers\<PRINTER_NAME>\Printer Driver
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Print\Providers\Client Side Rendering Print Provider\Servers\<PRINTER_NAME>\Printers\<GUID>
Calculating the driver name is some nuanced code, but can be used reliably. For a comprehensive code example, see here. Take special note of the special character replacement in the keys/values.
Once the driver is found, matching the terms Ricoh
, HP
, PCL6
and PCL 6
using a regular expression will allow filtering trayIDs over 1,000.
By combining the above techniques of trayId numbering, vendor matching and the keywords "PCL6" and "PCL 6", the bad trays can be filtered out programatically.