Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get notification from printer when physical page/job is complete

I'd like to be able to get a notification from a network printer when it physically finishes printing a page (and/or the entire job). This is going to be used in an app I'm writing for print management via web, and since users get charged per-page, and the charge shouldn't go down before the page actually completes.

I'm not sure whether this requires writing a driver, some kind of plug-in or whether a client app will do. I am flexible with my platform, since my client isn't written yet, so I'd like to hear about any suitable solution in either Windows or Linux, in any programming language/level.

I'm aware that there is a difference between the spooler and the printer. I'm trying to examine at which level the printer might notify the machine, via IPP, when the page or job physically complete.

I'm currently looking into Java, using either jspi or cups4j package to obtain a notification when the IPP property job-impressions-completed changes, or alternatively, poll for it. I'm using a CUPS IPP interface to a local printer. Running a simple tester (HelloPrint.java attached below; or the CupsTest.java included in cups4j), I did not receive any job-impressions-completed attribute changes nor did it list the attribute for the job when I was polling.

So here are the questions:

  • Am doing this right? If not, then how should I do it?
  • Since this is a CUPS interface to a local printer, it might be that the job-impressions-completed attribute isn't being updated, specifically since it acts as a spooler for the real printer. Assuming that the real printer will notify about or list this attribute, Would this be printer-specific or must any IPP-supporting printer have this attribute available and updated?

System info: Ubuntu 11.10, CUPS 1.5.0, printer is Brother HL-2240D (PPD available here)

Note: HL-2240D is not the printer I will be using for the eventual project (specifically, it doesn't support IPP); I'm intending to use either an HP HL4250DN or Samsung 3741ND or similar.

Here's a sample app using the javax.print packages and jspi:

HelloPrint.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;

import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.print.event.*;

import de.lohndirekt.print.IppPrintService;

public class HelloPrint {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // create request attributes
        PrintRequestAttributeSet requestAttributeSet = new HashPrintRequestAttributeSet();
        requestAttributeSet.add(MediaSizeName.ISO_A4);
        requestAttributeSet.add(new Copies(1));
        requestAttributeSet.add(Sides.DUPLEX);

        // find an appropriate service
        // using jspi (http://code.google.com/p/jspi/)
        URI printerURI;
        try {
            printerURI = new URI("ipp://localhost:631/printers/HL2240D-local");
        } catch (URISyntaxException e2) {
            e2.printStackTrace();
            return;
        }
        IppPrintService service = new IppPrintService(printerURI);

        // by enumerating       
        //      PrintService[] services = PrintServiceLookup.lookupPrintServices(
        //              DocFlavor.INPUT_STREAM.PDF, requestAttributeSet);
        //      for (PrintService service1 : services) {
        //          System.out.println(service1);
        //      }
        //      PrintService service = services[0];

        // add listeners to service
        service.addPrintServiceAttributeListener(new PrintServiceAttributeListener() {
            @Override
            public void attributeUpdate(PrintServiceAttributeEvent event) {
                PrintServiceAttributeSet serviceAttributeSet = event
                        .getAttributes();
                StringBuilder s = new StringBuilder();
                s.append("=== PrintServiceAttributeEvent: (" + serviceAttributeSet.size() + " attributes)\n");
                for (Attribute attribute : serviceAttributeSet.toArray()) {
                    PrintServiceAttribute printServiceAttribute = (PrintServiceAttribute) attribute;

                    s.append(printServiceAttribute.getCategory().getName()
                            + "/" + printServiceAttribute.getName() + " = "
                            + printServiceAttribute.toString() + "\n");

                }
                System.out.println(s.toString());
            }
        });

        // add file (blank.pdf is a blank page exported as PDF from LibreOffice
        // Writer)
        FileInputStream inputStream;
        try {
            inputStream = new FileInputStream("blank.pdf");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }

        // create a new doc and job
        DocAttributeSet docAttributeSet = new HashDocAttributeSet();
        docAttributeSet.add(MediaSizeName.ISO_A4);
        docAttributeSet.add(Sides.DUPLEX);

        Doc doc = new SimpleDoc(inputStream, DocFlavor.INPUT_STREAM.PDF,
                docAttributeSet);

        DocPrintJob job = service.createPrintJob();

        // listen to print job attribute change events
        // attribute set is null, means this means to listen on all dynamic
        // attributes that the job supports.
        job.addPrintJobAttributeListener(new PrintJobAttributeListener() {
            @Override
            public void attributeUpdate(PrintJobAttributeEvent event) {
                PrintJobAttributeSet jobAttributeSet = event.getAttributes();
                StringBuilder s = new StringBuilder();
                s.append("=== PrintJobAttributeEvent: (" + jobAttributeSet.size() + " attributes)\n");
                for (Attribute attribute : jobAttributeSet.toArray()) {
                    PrintJobAttribute jobAttribute = (PrintJobAttribute) attribute;

                    s.append(jobAttribute.getCategory().getName() + "/"
                            + jobAttribute.getName() + " = "
                            + jobAttribute.toString() + "\n");

                }
                System.out.println(s.toString());

            }
        }, null);

        // listen to print job events
        job.addPrintJobListener(new PrintJobListener() {

            @Override
            public void printJobRequiresAttention(PrintJobEvent pje) {
                System.out.println("=== PrintJobEvent: printJobRequiresAttention");
            }

            @Override
            public void printJobNoMoreEvents(PrintJobEvent pje) {
                // TODO Auto-generated method stub
                System.out.println("=== PrintJobEvent: printJobNoMoreEvents");
                System.out.println(pje.getPrintEventType());
                System.out.println(pje.toString());
            }

            @Override
            public void printJobFailed(PrintJobEvent pje) {
                // TODO Auto-generated method stub
                System.out.println("=== PrintJobEvent: printJobFailed");
                System.out.println(pje.getPrintEventType());
                System.out.println(pje.toString());
            }

            @Override
            public void printJobCompleted(PrintJobEvent pje) {
                // TODO Auto-generated method stub
                System.out.println("=== PrintJobEvent: printJobCompleted");
                System.out.println(pje.getPrintEventType());
                System.out.println(pje.toString());
            }

            @Override
            public void printJobCanceled(PrintJobEvent pje) {
                // TODO Auto-generated method stub
                System.out.println("=== PrintJobEvent: printJobCanceled");
                System.out.println(pje.getPrintEventType());
                System.out.println(pje.toString());
            }

            @Override
            public void printDataTransferCompleted(PrintJobEvent pje) {
                System.out.println("=== PrintJobEvent: printDataTransferCompleted");
                System.out.println(pje.getPrintEventType());
                System.out.println(pje.toString());
            }
        });

        // print
        try {
            job.print(doc, requestAttributeSet);
        } catch (PrintException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
            return;
        }
        
        // try polling
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return;
            }
            
            System.out.println("=== Polling: I'm alive and it's " + new Date());
            System.out.println("Job attributes");
            for (Attribute attribute : job.getAttributes().toArray()) {
                System.out.println((attribute.getCategory().getName() + "/"
                        + attribute.getName() + " = " + attribute.toString()));
            }
            System.out.println("Service attributes");
            for (Attribute attribute : service.getAttributes().toArray()) {
                System.out.println((attribute.getCategory().getName() + "/"
                        + attribute.getName() + " = " + attribute.toString()));
            }
        }
    }

}
like image 615
Yuval Avatar asked Apr 10 '12 11:04

Yuval


People also ask

How do I get notifications for my printer?

Select a printer, and, at the top of the window, click Print server properties. From the Advanced tab, check or uncheck Show informational notifications for local printers and Show informational notifications for network printers.

How do I turn on HP printer notifications?

Open the Devices and Printers folder, right-click the HP UPD, and then select Properties. Click the Device Settings tab. In the Installable Options section, set Printer Status Notification to either Enable or Disable. Click OK.


1 Answers

In the end it all depends on the printers firmware. IPP specifies the attribute job-impressions-completed as optional. That means if the printer can't tell which page has been printed, you won't be able to read it - no matter whether your programming is correct or not.

Manufacturers usually claim to support IPP but don't document very well the optional parts they might have implemented (or not).

Before doing any programming I suggest to read all available job-attributes using the ipptool available from CUPS:

#!/usr/bin/env ipptool -tv -d job=482 ipp://192.168.2.113/ipp
{
OPERATION Get-Job-Attributes
GROUP operation-attributes-tag
ATTR charset attributes-charset utf-8
ATTR language attributes-natural-language en
ATTR uri printer-uri $uri
ATTR integer job-id $job
}

job-state is a mandatory attribute and should reach a final state after some time: completed, aborted or canceled. This might be good enough, if you can get the number of job-pages somewhere else.

Implementation hint: IppJob provides method waitForTermination()

like image 57
IPP Nerd Avatar answered Sep 28 '22 01:09

IPP Nerd