使用 javax.print 库打印属性(托盘控制、双面打印等)
Posted
技术标签:
【中文标题】使用 javax.print 库打印属性(托盘控制、双面打印等)【英文标题】:Printing with Attributes(Tray Control, Duplex, etc...) using javax.print library 【发布时间】:2012-12-28 23:27:49 【问题描述】:一段时间以来,我一直在尝试确定一种使用标准 Java Print 库打印文件的方法 - 特别是 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();
简而言之,您执行以下操作
-
查找打印机
创建 PrinterJob
创建属性集
向 AttributeSet 添加属性,例如 Tray 和 Duplex
使用 AttributeSet 在打印机作业上调用 print
这里的问题是,尽管有记录的方法,以及我从几个教程中发现的方法,但这种方法...不起作用。现在请记住,我知道这听起来不太描述,但请听我说。 我不是随便说的...
The official documentation for PrinterJob 实际上提到 AttributeSet 在默认实现中被忽略。 Source code seen here 表明这是真的 - 属性被传入并完全忽略。
那么显然,您需要该类的某种扩展版本,它可能基于特定的打印机及其功能?我试图编写一些测试代码来告诉我这些功能 - 我们在办公室设置了各种各样的打印机,无论大小,简单或充满花里胡哨 - 更不用说我电脑上的几个驱动程序只是为了伪- 打印机驱动程序,无需任何硬件即可创建文档和模拟打印机。测试代码如下:
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 简单设置但从未运行到完成状态吗?如果是这样,还有其他选择吗?
【问题讨论】:
哈哈。一个多星期没有回应,然后在赏金的最后几个小时内投票飙升。呵呵。 【参考方案1】:问题在于 Java 打印 API 是世界之间的桥梁。打印机制造商不发布 JVM 驱动程序。他们发布了适用于 Windows、Macintosh 的驱动程序,并且也许有人拥有适用于一个或多个 *nix 平台的给定打印机的驱动程序。
随之而来的是一些在某个主机系统上的 JVM 中运行的 Java 代码。当您开始查询打印机功能时,您并不是在与打印机对话——您是在与 java.awt.print 中的一个桥接类对话,该桥接类连接到 JVM,它连接到主机操作系统,它连接到任何特定的为给定的打印机安装了驱动程序。所以有几个地方可能会崩溃......您所在的特定 JVM 可能会也可能不会完全实现用于查询打印机功能的 API,更不用说为给定的作业传递这些参数了。
一些建议:
-
查看 javax.print 类作为替代
java.awt.print——我从那里打印的运气更好。
尝试为您的打印机使用替代打印驱动程序 -- 您可以定义
到给定打印机的多个命名连接,每个连接都有不同的
司机。如果您有制造商提供的驱动程序,请尝试更通用的驱动程序,如果您有通用驱动程序,请尝试安装更具体的驱动程序。
在您的平台的替代 JVM 实现下运行您的代码
【讨论】:
通过虚拟成为这里唯一的人,我授予你赏金! ......不过,说真的,这不是一个糟糕的答案。【参考方案2】:因此,我们不可避免地找到了一种使用不同设置打印到不同托盘的方法,但不是直接打印。我们发现无法通过 printJob.print 方法发送属性,而这并没有改变。但是,我们能够设置打印作业的名称,然后使用低级 Perl 脚本拦截打印作业,解析名称,并在那里设置纸盘和双面打印设置。这是一个极端的黑客,但它有效。 Java 打印机属性不起作用仍然是事实,如果要设置它们,您将需要找到其他方法。
【讨论】:
你能分享你的代码吗?我有完全相同的问题,但没有任何 perl 知识。 @michdraft 抱歉,我不能 - 这是一个基于公司的问题,另一个员工负责 Perl 脚本(它还做许多其他事情)。不过,从概念上讲,这很简单——我们将 Java 中的“打印作业名称”字符串设置为我们自己设计的可解析模式,然后让 Perl 脚本解析它并设置双工选项。 你能澄清一下你所说的“拦截打印作业”是什么意思吗?你是怎么做到的? 恐怕我不能澄清太多。我所知道的是,一位同事设计的 Perl 脚本会自动对出现在低系统级别的打印作业做出反应,然后能够在它们继续之前对其进行更改。这实际上是如何工作的超出了我的理解。 @amaidment 我使用 PJL commands to printer 用纯 Java(无库)解决了这个问题,我相信现在大多数打印机都接受 PJL。javax.print
库结果更让人头疼。【参考方案3】:
我们对打印 PDF 有类似的要求,希望将一些页面发送到特定托盘,还希望将文档装订。 我们使用Java代码+ghost脚本组合 首先将 PDF 转换为 ghost 脚本,然后将 PJL(打印作业语言)命令添加到 ghost 脚本文件以选择托盘并装订文档。 然后将编辑后的 ghost 脚本文件发送到打印机。
这是用Java编写的完整示例
http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html
-拉姆
【讨论】:
此外,我还从 bfo.com 找到了这个 jars,它支持使用纯 Java 进行双面打印,仅用于 PDF。但它的商业。您需要付款。 bfo.com/blog/2012/02/15/using_java_to_print_pdf_documents.html【参考方案4】:这是 javafx Tray 中的样子,可能会有所不同,它还会打印出所有可用的托盘,只需更改托盘名称即可
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
【讨论】:
【参考方案5】:我发现打印机托盘的诀窍是使用getSupportedAttributeValues(...)
遍历Media.class
,匹配人类可读的名称,然后选择该特定值。在具有多种托盘配置的 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);
【讨论】:
以上是关于使用 javax.print 库打印属性(托盘控制、双面打印等)的主要内容,如果未能解决你的问题,请参考以下文章