打印机的打印方法何时完成文件打印?

3

我正在开发一个JAVA程序,用于打印文件。我需要知道打印机何时完成打印文件,我发现了这个看起来有趣的简单代码:

import javax.print.*;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.print.attribute.standard.PrinterStateReason;
import javax.print.attribute.standard.PrinterStateReasons;
import javax.print.attribute.standard.Severity;
import javax.print.event.*;
import java.awt.*;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.*;
import java.util.Set;

/**
 * PrintTest
 */
public class PrintTest implements PrintServiceAttributeListener,PrintJobListener,Doc, Printable, PrintJobAttributeListener {

  private static final transient String TEXT = "12345";

  public static void main(String[] args) {
    PrintTest test = new PrintTest();
    test.checkPrinters();
  }

  public void checkPrinters() {
    Thread newThread = new Thread(new Runnable() {
      public void run() {
    PrintService ps = PrinterJob.getPrinterJob().getPrintService();

    DocFlavor[] myFlavors = ps.getSupportedDocFlavors();
    ps.addPrintServiceAttributeListener(PrintTest.this);
    DocPrintJob docJob = ps.createPrintJob();
      docJob.addPrintJobAttributeListener(PrintTest.this, null);
    docJob.addPrintJobListener(PrintTest.this);
    try {
      docJob.print(PrintTest.this,null);
    }
    catch (PrintException e) {
      e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
    } });

    newThread.start();
    /**
    PrintServiceAttributeSet attSet = ps.getAttributes();
    PrinterStateReasons psr = ps.getAttribute(PrinterStateReasons.class);

    if (psr != null) {
      Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
      for (PrinterStateReason reason : errors)
        System.out.printf(" Reason : %s",reason.getName());
      System.out.println();
    }          */
  }

  public void attributeUpdate(PrintServiceAttributeEvent psae) {
    System.out.println(psae.getAttributes());
  }

  public void printDataTransferCompleted(PrintJobEvent pje) {
    System.out.println("Transfer completed");
  }

  public void printJobCompleted(PrintJobEvent pje) {
    System.out.println("Completed");
  }

  public void printJobFailed(PrintJobEvent pje) {
    System.out.println("Failed");
    PrinterStateReasons psr = pje.getPrintJob().getPrintService().getAttribute(PrinterStateReasons.class);
    if (psr != null) {
      Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
      for (PrinterStateReason reason : errors)
        System.out.printf(" Reason : %s",reason.getName());
      System.out.println();
    }
  }

  public void printJobCanceled(PrintJobEvent pje) {
    System.out.println("Canceled");
  }

  public void printJobNoMoreEvents(PrintJobEvent pje) {
    System.out.println("No more events");
  }

  public void printJobRequiresAttention(PrintJobEvent pje) {
    System.out.println("Job requires attention");
    PrinterStateReasons psr = pje.getPrintJob().getPrintService().getAttribute(PrinterStateReasons.class);
    if (psr != null) {
      Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
      for (PrinterStateReason reason : errors)
        System.out.printf(" Reason : %s",reason.getName());
      System.out.println();
    }
  }

  public DocFlavor getDocFlavor() {
    return DocFlavor.SERVICE_FORMATTED.PRINTABLE;  //To change body of implemented methods use File | Settings | File Templates.
  }

  public Object getPrintData() throws IOException {
    return this;
  }

  public DocAttributeSet getAttributes() {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  public Reader getReaderForText() throws IOException {
    return null; //To change body of implemented methods use File | Settings | File Templates.
  }

  public InputStream getStreamForBytes() throws IOException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
    return pageIndex == 0 ? PAGE_EXISTS : NO_SUCH_PAGE;  //To change body of implemented methods use File | Settings | File Templates.
  }

  public void attributeUpdate(PrintJobAttributeEvent pjae) {
    System.out.println("Look out");
  }
}

所以,我感兴趣的方法是这个:
public void printJobCompleted(PrintJobEvent pje) {
    System.out.println("Completed");
  }

但这对我没有用。问题是打印机不支持这种类型的代码。有人能告诉我一个支持这种代码的打印机型号吗?


错误信息是什么?你检查了控制台吗?(我假设这是运行时错误而不是编译错误) - blurfus
1
就我个人而言,我不记得曾经有过一个应用程序(除了操作系统的打印队列管理器)在我的打印作业完成时通知我。因此,我猜想这很少,也许从未被实现。 - ControlAltDel
printJobNoMoreEvents(PrintJobEvent pje) 被调用以通知客户端不会再有事件被传递。生成此事件的原因之一是作业已成功完成,但打印系统在能力上受到限制,无法验证此情况。如果没有传递任何其他终端事件(已完成/失败/取消),则必须传递此事件。我认为,如果我们调用了 printJobNoMoreEvents(PrintJobEvent pje),就可以假定我们的打印成功了。 - Lyju I Edwinson
3个回答

0

看一下这里的示例代码here

看起来你需要实现你的PrintJobAdapter。相关部分如下:

package com.your.package;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.event.PrintJobAdapter;
import javax.print.event.PrintJobEvent;

public class DetermineThatPrintJobHasFinished {

    public static void main(String[] args) throws Exception {
       //...    see example in link

      // Locate the default print service for this environment.    
      PrintService service = PrintServiceLookup.lookupDefaultPrintService();

      // Create and return a PrintJob capable of handling data from    
      // any of the supported document flavors.    
      DocPrintJob printJob = service.createPrintJob();

      // register a listener to get notified when the job is complete    
      JobCompleteMonitor monitor = new JobCompleteMonitor();    
      printJob.addPrintJobListener(monitor);

      // Construct a SimpleDoc with the specified print data, doc flavor and doc attribute set.    
      Doc doc = new SimpleDoc(is, flavor, null);

      // Print a document with the specified job attributes.    
      printJob.print(doc, null);    
      monitor.waitForJobCompletion();

      is.close();
    }

    private static class JobCompleteMonitor extends PrintJobAdapter {   
        private boolean completed = false;

        @Override
        public void printJobCanceled(PrintJobEvent pje) {
            signalCompletion();
        }

        @Override
        public void printJobCompleted(PrintJobEvent pje) {
            signalCompletion();
        }

        @Override
        public void printJobFailed(PrintJobEvent pje) {
            signalCompletion();
        }

        @Override
        public void printJobNoMoreEvents(PrintJobEvent pje) {
            signalCompletion();
        }

        private void signalCompletion() {
           synchronized (JobCompleteMonitor.this) { 
               completed = true;    
               JobCompleteMonitor.this.notify();    
           }
        }

        public synchronized void waitForJobCompletion() {    
            try {
                while (!completed) {
                    wait();
                }
            } catch (InterruptedException e) {
            }
        }
    }
}

我尝试了你的代码,但问题没有解决。当我运行程序时,它会将文件发送到打印机,然后终止。我需要程序等待直到打印机终止才打印文件,然后程序必须调用方法PrintJobComplete。 - user2062413

0

虽然不是最高效的方法,但你可以等待属性“queued-job-count”等于零:

public final static void waitEndJobs(final PrintService printService) throws InterruptedException {

    int queue = 1;
    while (queue != 0) {
        final AttributeSet attributes = printService.getAttributes();
        final Attribute a = Arrays.stream(attributes.toArray())//
                .filter(o -> o.getName().equalsIgnoreCase("queued-job-count"))//
                .findFirst()//
                .orElse(null);
        queue = Integer.parseInt(attributes.get(a.getClass()).toString());
        Thread.sleep(5000);
    }
}

0

就我而言,Rx或Project Reactor可以非常有助于解决旧的Java打印机API问题。最近我也遇到了这个问题,目前我已经完成了下一个版本。也许对某些人会有所帮助。

首先,如果您像我一样使用pdfbox,则

@Slf4j
@Service
@RequiredArgsConstructor
public class PrinterDeviceServiceImpl implements PrinterDeviceService {

    @Override
    public StreamEx<PrintService> getAvailablePrinters() {
        return of(PrintServiceLookup.lookupPrintServices(null, null));
    }

    @Override
    public boolean isPrinterExists(String printerName) {
        return getAvailablePrinters()
                .anyMatch(p -> p.getName().equals(printerName));
    }

    @Override
    public PrintService getPrinter(String printerName) {
        return getAvailablePrinters()
                .findFirst(p -> p.getName().equals(printerName))
                .orElse(null);
    }

    @Override
    public Mono<PrintTaskStatus> print(String printerName, InputStream source) {
        try {
            PrintService printer = getPrinter(printerName);
            if (printer == null) {
                return Mono.error(new RuntimeException("printer is null"));
            }

            PDDocument document = Unchecked.supplier(() -> PDDocument.load(source));

            PrinterJob job = PrinterJob.getPrinterJob();

            job.setPageable(new PDFPageable(document, Orientation.PORTRAIT));
            job.setPrintService(printer);

            Paper paper = new Paper();
            paper.setSize(500, 500); //TODO: lets get it from configs or task request
            paper.setImageableArea(0.0, 0.0, paper.getWidth(), paper.getHeight()); // no margins

            // custom page format
            PageFormat pageFormat = new PageFormat();
            pageFormat.setPaper(paper);

            Book book = new Book();

            book.append(new PDFPrintable(document), pageFormat, document.getNumberOfPages());
            job.setPageable(book);

            HashPrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
            attr.add(OrientationRequested.PORTRAIT);
            attr.add(PrintQuality.HIGH);
            job.print(attr);

            return Mono.create(sink -> {
                /*If exec thread is UI or from http pool, then maybe better idea is to attach it to another independent one or run print in another one!*/
                while (!isPrintCompleted(printer)) {
                    log.debug("I'm waiting to printer processed queue");
                    Unchecked.runnable(() -> Thread.sleep(1000));
                }
                /*you can wait some fixed or dynamic time based on how big your document etc and finish with error state if it needed*/
                log.debug("printer queue have processed :)");
                sink.success(PrintTaskStatus.Completed);
            });
        } catch (Throwable ex) {
            ExceptionHelpers.logError(ex, log);
            return Mono.just(PrintTaskStatus.Failed);
        }
    }

    private boolean isPrintCompleted(PrintService printer) {
        return Arrays.stream(printer.getAttributes().toArray())
                .filter(att -> att instanceof QueuedJobCount)
                .map(QueuedJobCount.class::cast)
                .findFirst()
                .map(queuedJobCount -> queuedJobCount.getValue() == 0)
                .orElse(false);
    }
}

其次,如果您只喜欢默认打印机API,则将打印方法替换为:
    @Override
    public Mono<PrintTaskStatus> print(String printerName, InputStream source) {
        PrintService printer = getPrinter(printerName);
        if (printer == null) {
            return Mono.error(new RuntimeException("printer is null"));
        }

        try {
            DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
            DocPrintJob printJob = printer.createPrintJob();

            CustomPrintJobListener monitor = new CustomPrintJobListener();
            printJob.addPrintJobListener(monitor);

            Doc doc = new SimpleDoc(source, flavor, null);

            PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
            pras.add(OrientationRequested.PORTRAIT);
            pras.add(PrintQuality.NORMAL);
            pras.add(new Copies(1));
            printJob.print(doc, pras);

            return monitor.getJobMono();
        } catch (Exception ex) {
            ExceptionHelpers.logError(ex, log);
            return Mono.just(PrintTaskStatus.Failed);
        }
    }

    @Slf4j
    private static class CustomPrintJobListener extends PrintJobAdapter {
        private final EmitterProcessor<PrintTaskStatus> emitter = EmitterProcessor.create();

        @Override
        public void printJobCanceled(PrintJobEvent pje) {
            emitter.onNext(PrintTaskStatus.Canceled);
            log.info("Print job canceled :| ");
        }

        @Override
        public void printDataTransferCompleted(PrintJobEvent pje) {
            log.info("Print data transfer completed ;) ");
        }

        @Override
        public void printJobCompleted(PrintJobEvent pje) {
            emitter.onNext(PrintTaskStatus.Completed);
            log.info("Print job completed :) ");
        }

        @Override
        public void printJobFailed(PrintJobEvent pje) {
            emitter.onNext(PrintTaskStatus.Failed);
            log.info("Print job failed :( ");
        }

        @Override
        public void printJobNoMoreEvents(PrintJobEvent pje) {
            emitter.onNext(PrintTaskStatus.Completed);
            log.info("No more events to the job :|");
        }

        @Override
        public void printJobRequiresAttention(PrintJobEvent pje) {
            log.info("Print job requires attention :O ");
        }

        public Mono<PrintTaskStatus> getJobMono() {
            return emitter.next();
        }
    }

祝你好运 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接