10

Using Java, I need to print on network printer, which is not installed locally. I only know the printer name. All tutorials I've seen started with something like:

PrintService []services = PrinterJob.lookupPrintServices();

The problem is there can be no printers installed, so the services will be empty in that case. I need to set the printer name directly, not just enumerate through visible printers.

JoshDM
  • 4,939
  • 7
  • 43
  • 72
user1431544
  • 155
  • 1
  • 2
  • 8
  • Printers available through 'lookupPrintServices()' lists printers registered to the O/S and registered to the user who is running the application. For example, if in Windows, you need to make sure the Printer you want is registered as whichever active directory user the app is running as (local user, registered service, etc.) This is a one-time registration. On a network, you can also register a printer to the network and access it //server/printername – JoshDM Jun 27 '12 at 16:18
  • I dont need to use lookupPrintServices(). I've got printer names in //server/printername format. Problem is, that lookupPrintServices cannot see any of them and I cannot register or install it. In .NET you can simply set printer name in object printersettings. I want to know, if some similar possibility exists in Java too. It's simple. I've got pdf file (which handles PDFBox) and printer name and I want to silently print it without opening Acrobat. I want to know simplest way. – user1431544 Jun 27 '12 at 16:50
  • 1
    Even if it is registered to the network, if the printer isn't visible in the windows control panel for the domain user running the java app, it won't be visible to Java via the awt print api. You may have the name and the correct path, but if it's not registered to the server the app is running on, it won't be found. – JoshDM Jun 27 '12 at 18:25

2 Answers2

12

Java AWT Printing will not find a printer via a path if it is not registered to the Windows / Active Directory user that is running the printing application. You must register the printer path through Windows "Devices and Printers" as a printer for that user for it to be visible. Then, as that user, you must run lookupPrintServices to see the list of printers available and retrieve the proper PrintService by the exact name String listed.

/**
 * Retrieve the specified Print Service; will return null if not found.
 * @return
 */
public static PrintService findPrintService(String printerName) {

    PrintService service = null;
    
    // Get array of all print services - sort order NOT GUARANTEED!
    PrintService[] services = PrinterJob.lookupPrintServices();
    
    // Retrieve specified print service from the array
    for (int index = 0; service == null && index < services.length; index++) {
        
        if (services[index].getName().equalsIgnoreCase(printerName)) {

            service = services[index];
        }
    }

    // Return the print service
    return service;
}

/**
 * Retrieve a PrinterJob instance set with the PrinterService using the printerName.
 * 
 * @return
 * @throws Exception IllegalStateException if expected printer is not found.
 */
public static PrinterJob findPrinterJob(String printerName) throws Exception {

    // Retrieve the Printer Service
    PrintService printService = PrintUtility.findPrintService(printerName);

    // Validate the Printer Service
    if (printService == null) {

        throw new IllegalStateException("Unrecognized Printer Service \"" + printerName + '"');
    }
    
    // Obtain a Printer Job instance.
    PrinterJob printerJob = PrinterJob.getPrinterJob();
    
    // Set the Print Service.
    printerJob.setPrintService(printService);

    // Return Print Job
    return printerJob;
}

/**
 * Printer list does not necessarily refresh if you change the list of 
 * printers within the O/S; you can run this to refresh if necessary.
 */
public static void refreshSystemPrinterList() {

    Class[] classes = PrintServiceLookup.class.getDeclaredClasses();

    for (int i = 0; i < classes.length; i++) {

        if ("javax.print.PrintServiceLookup$Services".equals(classes[i].getName())) {

            sun.awt.AppContext.getAppContext().remove(classes[i]);
            break;
        }
    }
}
JoshDM
  • 4,939
  • 7
  • 43
  • 72
  • 1
    So you are trying to tell me, that there is no way to print on unregistered printer? Thats great. Thank you. – user1431544 Jun 27 '12 at 18:50
  • Why are you restricted from registering the network printer to the server where the application runs? It is a one-time task. – JoshDM Jun 27 '12 at 21:57
  • Honestly, I don't know. It's customers network and he has got some reason for it. I know it sucks, bu registering printers is not an option. – user1431544 Jun 28 '12 at 09:49
  • But it's your customer's printer and your customer's server and... does your customer understand how this sort of thing actually works? – JoshDM Jun 28 '12 at 15:04
  • He only knows that his old system can handle this:) I can print in .NET (application is written in it) with ComponentOne. Problem is, that this type of printing takes a very long time (minutes). When printer is registered, it can print in a matter of seconds (on the same physical printer). So I wanted to try it with another component and find out, that exists PDFBox, which can print pdfs, but is originaly written in Java. So now, I'm using IKVM and trying to find out how to create PrintJob, which PDFBox's print method takes as an argument. – user1431544 Jun 28 '12 at 15:43
  • PDFBox takes a `PrinterJob` according to the code. The code I posted in my answer above shows how to create a `PrinterJob`. I am uncertain that without registering the printer, you can do it other ways. I suspect the reason it worked (slowly) with unregistered printer is that the printer was registering in the background, then unregistering in the background when complete, but I don't know enough to confirm that. – JoshDM Jun 28 '12 at 19:14
  • Maybe, its hard to find out. We don't have this problem in our network (max 20 seconds). Yes, I need PrintJob. But its true... how can I create print job, if I don't have printer:) – user1431544 Jun 28 '12 at 21:51
  • `lookupPrintServices` is not guaranteed to return any order or size, as the values available to the Windows user at any given time may vary; you will need to sort them yourself. `AppContext` is `sun.awt.AppContext`. – JoshDM Aug 07 '15 at 19:41
  • 1
    Unregistered printers can (generally) be written to directly using a Socket on port 9100. This does require you to know the low-level data that the printer is expecting. Many office printers can handle PDFs natively (make sure to get the size right, PDFBOX handles sizing using the driver, which you're bypassing by using an unregistered printer). – tresf Aug 10 '19 at 15:48
2

In my case there was an authentication error, I could not find shared printer because i searched them with LocalUser account, using another account or changing printer grants I could find it.

Tobia
  • 9,165
  • 28
  • 114
  • 219