Home > OS >  Why does getSupportedAttributeValues return Paper Types
Why does getSupportedAttributeValues return Paper Types

Time:03-25

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 and PCL6|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 through 248 are selected by substituting the values 8 through 255 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 of 1025 = Auto Tray Select, but this doesn't seem to be the case for their PCL6 driver, which has the MediaType 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.

  • Related