使用javax.print库以属性(托盘控制,双工等)打印

时间:2013-01-14 22:33:38

标签: java pdf printing

我一直在努力确定一种方法来使用标准Java打印库打印文件 - 特别是PDF文档 - 具有某些属性 - 具体而言,某些托盘或使用双面打印。

关于如何做到这一点有大量的文档,事实上,我已经研究并尝试了这些方法。典型的方式是这样的:

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();
    }
}

简而言之,您可以执行以下操作

  1. 查找打印机
  2. 创建PrinterJob
  3. 创建一个AttributeSet
  4. 将属性添加到AttributeSet,例如Tray和Duplex
  5. 使用AttributeSet
  6. 在打印机作业上调用打印

    这里的问题是,尽管有记录的方式,以及我从几个教程中发现的,这种方法... 不起作用。现在请记住,我知道听起来并不是很有描述,但是请听我说。 我不轻易说 ......

    The official documentation for PrinterJob实际上提到在默认实现中忽略AttributeSet。 Source code seen here表明这是真的 - 属性被传入并完全被忽略。

    显然,你需要某种类的扩展版本,这可能是基于特定的打印机及其功能?我试图编写一些测试代码,告诉我这些功能 - 我们在办公室设置了各种各样的打印机,无论大小,简单或充满了花里胡哨 - 更不用说我计算机上的几个驱动程序只是伪-printer驱动程序,只需创建文档和模拟打印机,无需使用任何类型的硬件。测试代码如下:

    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"));
    }
    

    我发现的结果是每台打印机都毫无例外地返回支持“副本”,而其他所有属性都没有。此外,无论看起来多么难以置信,每台打印机的功能都是相同的。

    不可避免的问题是多层次的:如何以他们注册的方式发送属性?另外,如何正确检测打印机的功能?实际上,PrinterJob类实际上是以可用的方式扩展,还是属性总是被忽略?

    我在整个互联网上找到的例子似乎向我建议,后一个问题的答案是“不,他们总是被忽略”,这对我来说似乎很荒谬(但是当我筛选数百页时越来越可信了) )。 这个代码是Sun只是设置但从未工作到完成状态?如果是这样,还有其他选择吗?

5 个答案:

答案 0 :(得分:10)

问题在于Java print API是世界之间的桥梁。打印机制造商不会发布JVM的驱动程序。他们发布适用于Windows,Macintosh的驱动程序,也许某人有一个驱动程序用于在一个或多个* nix平台上运行的给定打印机。

随着您在一些主机系统上的JVM中运行一些Java代码。当您开始查询打印机功能时,您不是在与打印机通信 - 您正在与java.awt.print中的桥接类进行通信,该类连接到JVM,该JVM挂接到主机操作系统,该主机操作系统挂钩到任何特定的已为给定的打印机安装了驱动程序。因此,有几个地方可能会崩溃......您所使用的特定JVM可能会或可能不会完全实现用于查询打印机功能的API,更不用说为给定作业传递这些参数。

一些建议:

  1. 查看javax.print类作为替代 java.awt.print - 我从那里打印得更幸运了。
  2. 尝试为您的打印机使用替代打印驱动程序 - 您可以定义 给定打印机的多个命名连接,每个连接都有不同的 驱动程序。如果您有制造商提供的驱动程序,请尝试更通用的驱动程序,如果您有通用驱动程序,请尝试安装更具体的驱动程序。
  3. 在您的平台的备用JVM实施下运行您的代码

答案 1 :(得分:6)

因此,我们不可避免地找到了一种方法来打印到不同的托盘和不同的设置,但不是直接。我们发现无法通过printJob.print方法发送属性,而且还没有改变。但是,我们能够设置打印作业的名称,然后使用低级Perl脚本拦截打印作业,解析名称,并在那里设置托盘和双工设置。这是一个极端的黑客,但它的工作原理。仍然如此 Java打印机属性不起作用,如果你想设置它们,你需要找到另一种方式

答案 2 :(得分:1)

我们有类似的要求打印PDF并希望将一些页面发送到特定托盘,并且还希望对文档进行装订。 我们使用Java代码+ ghost脚本组合 首先将PDF转换为ghost脚本,然后将PJL(打印作业语言)命令添加到ghost脚本文件以选择托盘并装订文档。 然后将编辑过的ghost脚本文件发送到打印机。

这是用Java编写的完整示例

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

-RAM

答案 3 :(得分:1)

这里的内容在javafx中的样子可能会有所不同,它也会打印出所有可用的托盘只需更改托盘名称

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();
        }
    }
}

这是我的输出:

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

答案 4 :(得分:1)

我发现打印机纸盘的诀窍是使用Media.class遍历getSupportedAttributeValues(...),匹配人类可读的名称,然后选择该特定值。在Windows,具有多种任务栏配置的MacOS上进行了测试。

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);