Printing with Attributes(Tray Control, Duplex, etc...) using javax.print library

16,553

Solution 1

So, we inevitably found a way to print to different trays and with different settings, but not directly. We found it impossible to send attributes via the printJob.print method, and that much hasn't changed. However, we were able to set the name of the print job, then intercept the print job with a low-level Perl script, parse the name, and set the tray and duplex settings there. It's an extreme hack, but it works. It still remains true that Java Printer Attributes do not work, and you will need to find another way if you want to set them.

Solution 2

The problem is that the the Java print API is a bridge between worlds. Printer manufacturers don't release drivers for the JVM. They release drivers for Windows, Macintosh, and maybe someone has a a driver for a given printer that works on one or more *nix platforms.

Along you come with some Java code running inside a JVM on some host system. When you start querying the printer features, you aren't talking to the printers -- you are talking to a bridge class in java.awt.print that hook into the JVM, which hooks to the host operating system, which hooks into whatever particular driver was installed for a given printer. So there are several places where this can fall apart... The particular JVM you are on may or may not fully implement the API for querying printer features, let alone passing those parameters along for a given job.

A few suggestions:

  1. look into the javax.print classes as an alternative to java.awt.print -- I've had more luck printing from there.
  2. try using alternative print drivers for your printers -- you can define multiple named connections to a given printer, each with a different driver. If you've got a manufacturer provided driver, try a more generic driver, if you've got a generic driver, try to install a more specific one.
  3. run your code under alternate JVM implementations for your platform

Solution 3

We had similar requirement to print PDF's and wanted to send some pages to Specific tray and also wanted the document to be stapled. We used Java code + ghost script combination First convert PDF to ghost script and then add PJL (Print job language) commands to ghost script file to select trays and staple the documents. Then send that edited ghost script file to printer.

Here is complete example written in Java

http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html

-Ram

Solution 4

Here's what it looks like in javafx Tray's may vary and it will also print out all trays that are available just change the tray name

private void printImage(Node node) {
    PrinterJob job = PrinterJob.createPrinterJob();
    if (job != null) {
        JobSettings js = job.getJobSettings();
        PaperSource papersource = js.getPaperSource();
        System.out.println("PaperSource=" + papersource);
        PrinterAttributes pa = printer.getPrinterAttributes();
        Set<PaperSource> s = pa.getSupportedPaperSources();
        System.out.println("# of papersources=" + s.size());
        if (s != null) {
            for (PaperSource newPaperSource : s) {
                System.out.println("newpapersource= " + newPaperSource);
                //Here is where you would put the tray name that is appropriate
                //in the contains section
                if(newPaperSource.toString().contains("Tray 2"))
                    js.setPaperSource(newPaperSource);
            }
        }
        job.getJobSettings().setJobName("Whatever");
        ObjectProperty<PaperSource> sources = job.getJobSettings().paperSourceProperty();
        System.out.println(sources.toString());
        boolean success = job.printPage(node);
        if (success) {
            System.out.println("PRINTING FINISHED");
            job.endJob();
            //Stage mainStage = (Stage) root.getScene().getWindow();
            //mainStage.close();
        }
    }
}

Here's My output:

PaperSource=Paper source : Automatic
# of papersources=6
newpapersource= Paper source :
newpapersource= Paper source :  Manual Feed in Tray 1
newpapersource= Paper source :  Printer auto select
newpapersource= Paper source :  Tray 1
newpapersource= Paper source :  Tray 2
newpapersource= Paper source : Form-Source
ObjectProperty [bean:  Collation = UNCOLLATED
 Copies = 1
 Sides = ONE_SIDED
 JobName = Whatever
 Page ranges = null
 Print color = COLOR
 Print quality = NORMAL
 Print resolution = Feed res=600dpi. Cross Feed res=600dpi.
 Paper source = Paper source :  Tray 2
 Page layout = Paper=Paper: Letter size=8.5x11.0 INCH Orient=PORTRAIT leftMargin=54.0 rightMargin=54.0 topMargin=54.0 bottomMargin=54.0, name: paperSource, value: Paper source :  Tray 2]
PRINTING FINISHED

Solution 5

I've found the trick for the printer trays is to iterate over the Media.class using getSupportedAttributeValues(...), match the human-readable name, and select that particular value. Tested on Windows, MacOS with several tray configurations.

String tray = "1";

// Handle human-readable names, see PRINTER_TRAY_ALIASES usage below for context.  Adjust as needed.
List<String> PRINTER_TRAY_ALIASES = Arrays.asList("", "Tray ", "Paper Cassette ");

// Get default printer
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();

// Attributes to be provided at print time
PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();

Media[] supported = printService.getSupportedAttributeValues(Media.class, null, null);
for(Media m : supported) {           
    for(String pta : PRINTER_TRAY_ALIASES) {
        // Matches "1", "Tray 1", or "Paper Cassette 1"
        if (m.toString().trim().equalsIgnoreCase(pta + tray)) {
            attributes.add(m);
            break;
        }
    }
}

// Print, etc
// printJob.print(pdfDoc, pset);
Share:
16,553

Related videos on Youtube

Southpaw Hare
Author by

Southpaw Hare

Professional Programmer Java Python COBOL C/C++ Angular.js Game Developer Pygame Ren'py Competitive Gaming Enthusiast Yomi Super Smash Bros Marvel vs Capcom Dragon Ball FighterZ Street Fighter Civilization IV Divekick Challenge Game Lover XCOM 2 (+ Long War 2) Xenonauts Dwarf Fortress RimWorld Minecraft: Terrafirmacraft Don't Starve Pokemon: Nuzlocke Artemis: Starship Bridge Simulator Gauntlet Darkest Dungeon Roleplaying Game Enthusiast Dungeon World Anima: Beyond Fantasy Tails of Equestria D&amp;D 3rd Edition / 3.5 / Pathfinder D&amp;D 5th Edition D&amp;D 2nd Edition Old World of Darkness Street Fighter: The Storytelling Game Fandom Member My Little Pony: Friendship is Magic Furries

Updated on June 16, 2022

Comments

  • Southpaw Hare
    Southpaw Hare about 2 years

    I've been trying for some time to determine a way to use the standard Java Print library to print files - specifically, PDF documents - with certain attributes - specifically, to certain trays or using duplex.

    There exists plenty of documentation on how this should be done, and indeed, I've researched and tried these methods. The typical way is something like this:

    public static void main (String [] args) {
        try {
    
            PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);
    
            //Acquire Printer
            PrintService printer = null;
            for (PrintService serv: pservices) {
                System.out.println(serv.toString());
                if (serv.getName().equals("PRINTER_NAME_BLAH")) {
                    printer = serv;
                }
            }
    
            if (printer != null) {
                System.out.println("Found!");
    
    
                //Open File
                FileInputStream fis = new FileInputStream("FILENAME_BLAH_BLAH.pdf");
    
                //Create Doc out of file, autosense filetype
                Doc pdfDoc = new SimpleDoc(fis, DocFlavor.INPUT_STREAM.AUTOSENSE, null);
    
                //Create job for printer
                DocPrintJob printJob = printer.createPrintJob();
    
                //Create AttributeSet
                PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();
    
                //Add MediaTray to AttributeSet
                pset.add(MediaTray.TOP);
    
                //Add Duplex Option to AttributeSet
                pset.add(Sides.DUPLEX);
    
                //Print using Doc and Attributes
                printJob.print(pdfDoc, pset);
    
                //Close File
                fis.close();
    
            }
    
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
    

    In short, you do the following

    1. Find the Printer
    2. Create a PrinterJob
    3. Create an AttributeSet
    4. Add Attributes to the AttributeSet, such as Tray and Duplex
    5. Call print on the printer job using the AttributeSet

    The problem here is that, despite being the documented way of doing this, as well as what I've found from several tutorials, this method... doesn't work. Now keep in mind, I know that doesn't sound very descript, but hear me out. I don't say that lightly...

    The official documentation for PrinterJob actually mentions that the AttributeSet is ignored in the default implementation. Source code seen here shows this to be true - the attributes are passed in and ignored entirely.

    So apparently, you need some sort of extended version of the class, which is possibly based on the specific printers and their capabilities? I attempted to write some test code that would tell me such capabilities - we have a large variety of printers set up at the office, large or small, simple or full of bells and whistles - not to mention several drivers on my computer just for pseudo-printer drivers that just create documents and simulate printers without going to any sort of hardware. The test code is as follows:

    public static void main (String [] args) {
    
        PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);
    
        for (PrintService serv: pservices) {
            System.out.println(serv.toString());
    
            printFunctionality(serv, "Trays", MediaTray.class);
            printFunctionality(serv, "Copies", Copies.class);
            printFunctionality(serv, "Print Quality", PrintQuality.class);
            printFunctionality(serv, "Color", ColorSupported.class);
            printFunctionality(serv, "Media Size", MediaSize.class);
            printFunctionality(serv, "Accepting Jobs", PrinterIsAcceptingJobs.class);
        }
    }
    
    private static void printFunctionality(PrintService serv, String attrName, Class<? extends Attribute> attr) {
        boolean isSupported = serv.isAttributeCategorySupported(attr);
        System.out.println("    " + attrName + ": " + (isSupported ? "Y" : "N"));
    }
    

    The results I found were that every printer, without exception, returned that "copies" were supported, and all other attributes were not. Furthermore, every printer's capabilities were identical, regardless of how implausible that would seem.

    The inevitable question is multi-layered: How does one send in attributes in a way that they are registered? Additionally, how does one properly detect the capabilities of a printer? Indeed, is the PrinterJob class actually extended in a usable way at all, or are the Attributes always ignored?

    Examples I've found throughout The Internet seem to suggest to me that the answer to the latter question is "No, they are always ignored", which seems ridiculous to me (but increasingly more believable as I sift through hundreds of pages). Is this code that Sun simply set up but never got working to a completed state? If so, are there any alternatives?

    • Southpaw Hare
      Southpaw Hare over 11 years
      LOL. No response for over a week, and then the votes skyrocket in the last few hours of the bounty. Heheheh.
  • Southpaw Hare
    Southpaw Hare over 11 years
    By virtual of being the ONLY PERSON HERE, I grant you the bounty! ...in all seriousness though, not a half-bad answer.
  • michdraft
    michdraft over 10 years
    Could you please share you code? i have exact the same problem but have not any knowledge of perl.
  • Southpaw Hare
    Southpaw Hare over 10 years
    @michdraft Sorry, I cannot - this was a company-based issue, and another employee is responsible for the Perl scripts (which also do many other things). Conceptually, though, it is simple - we set the "Print Job Name" string in Java to be a parsable pattern of our own design, and then have the Perl script parse it and set the duplexing options.
  • Reddymails
    Reddymails over 9 years
    In addition I also found this jars from bfo.com which supports duplex printing using pure java for PDF'd only. But Its COMMERCIAL. You need to Pay. bfo.com/blog/2012/02/15/using_java_to_print_pdf_documents.ht‌​ml
  • amaidment
    amaidment over 8 years
    Can you clarify what you mean by "intercepting the print job"? How did you accomplish this?
  • Southpaw Hare
    Southpaw Hare over 8 years
    I am afraid I cannot clarify much. What I know is that a co-worker designed Perl scripts that automatically react to print jobs appearing at a low system level, and is then able to alter them before they proceed. How this actually works is beyond my understanding.
  • egerardus
    egerardus about 5 years
    @amaidment I solved this with pure Java (no libraries) using PJL commands to printer, I believe most printers accept PJL nowadays. The javax.print library turned out to be more of a headache.