java 使用 javax.print 库打印属性(托盘控制、双面打印等...)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14328012/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Printing with Attributes(Tray Control, Duplex, etc...) using javax.print library
提问by Southpaw Hare
I've been trying for some time to determine a way to use the standard Java Print libraryto print files - specifically, PDF documents - with certain attributes- specifically, to certain trays or using duplex.
一段时间以来,我一直在尝试确定一种使用标准 Java 打印库打印文件的方法——特别是 PDF 文档——具有某些属性——特别是某些托盘或使用双面打印。
There exists plenty of documentation on how this should be done, and indeed, I've researched and tried these methods. The typical way is something like this:
有很多关于如何做到这一点的文档,事实上,我已经研究并尝试过这些方法。典型的方法是这样的:
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();
}
}
In short, you do the following
简而言之,您执行以下操作
- Find the Printer
- Create a PrinterJob
- Create an AttributeSet
- Add Attributes to the AttributeSet, such as Tray and Duplex
- Call print on the printer job using the AttributeSet
- 查找打印机
- 创建打印机作业
- 创建一个属性集
- 给 AttributeSet 添加属性,比如 Tray 和 Duplex
- 使用 AttributeSet 在打印机作业上调用打印
The problem here is that, despite being the documented way of doing this, as well as what I've found from several tutorials, this method... doesn't work. Now keep in mind, I know that doesn't sound very descript, but hear me out. I don't say that lightly...
这里的问题是,尽管有记录的这样做的方式,以及我从几个教程中发现的,但这种方法......不起作用。现在请记住,我知道这听起来不太好描述,但请听我说完。 我不是随便说的...
The official documentation for PrinterJobactually mentions that the AttributeSet is ignored in the default implementation. Source code seen hereshows this to be true - the attributes are passed in and ignored entirely.
PrinterJob 的官方文档实际上提到在默认实现中忽略了 AttributeSet。 此处看到的源代码表明这是正确的 - 属性被传入并完全忽略。
So apparently, you need some sort of extended version of the class, which is possibly based on the specific printers and their capabilities? I attempted to write some test code that would tell me such capabilities - we have a large variety of printers set up at the office, large or small, simple or full of bells and whistles - not to mention several drivers on my computer just for pseudo-printer drivers that just create documents and simulate printers without going to any sort of hardware. The test code is as follows:
显然,您需要某种扩展版本的类,这可能基于特定的打印机及其功能?我试图编写一些测试代码来告诉我这些功能 - 我们在办公室设置了各种各样的打印机,或大或小,简单或充满花里胡哨 - 更不用说我电脑上的几个驱动程序只是为了伪- 打印机驱动程序,无需使用任何硬件即可创建文档和模拟打印机。测试代码如下:
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"));
}
The results I found were that every printer, without exception, returned that "copies" were supported, and all other attributes were not. Furthermore, every printer's capabilities were identical, regardless of how implausible that would seem.
我发现的结果是,每台打印机都无一例外地返回支持“副本”,而所有其他属性都不支持。此外,每台打印机的功能都是相同的,无论这看起来多么令人难以置信。
The inevitable question is multi-layered: How does one send in attributes in a way that they are registered? Additionally, how does one properly detect the capabilities of a printer? Indeed, is the PrinterJob class actually extended in a usable way at all, or are the Attributes always ignored?
不可避免的问题是多层次的:如何以注册的方式发送属性?此外,如何正确检测打印机的功能?实际上,PrinterJob 类实际上是以可用的方式扩展的,还是总是忽略属性?
Examples I've found throughout The Internet seem to suggest to me that the answer to the latter question is "No, they are always ignored", which seems ridiculous to me (but increasingly more believable as I sift through hundreds of pages). Is this code that Sun simply set up but never got workingto a completed state? If so, are there any alternatives?
我在整个互联网上找到的例子似乎向我暗示,后一个问题的答案是“不,它们总是被忽略”,这对我来说似乎很荒谬(但随着我筛选数百页,越来越可信)。 这段代码是 Sun 简单设置但从未运行到完成状态的代码吗?如果是这样,还有其他选择吗?
采纳答案by Southpaw Hare
So, we inevitably found a way to print to different trays and with different settings, but not directly. We found it impossible to send attributesvia the printJob.print method, and that much hasn't changed. However, we were able to set the name of the print job, then intercept the print job with a low-level Perl script, parse the name, and set the tray and duplex settings there. It's an extreme hack, but it works. It still remains true that Java Printer Attributes do not work, and you will need to find another wayif you want to set them.
因此,我们不可避免地找到了一种打印到不同托盘和不同设置的方法,但不是直接打印。我们发现无法通过 printJob.print 方法发送属性,而且这一点并没有改变。但是,我们能够设置打印作业的名称,然后使用低级 Perl 脚本拦截打印作业,解析名称,并在那里设置托盘和双面打印设置。这是一个极端的黑客,但它有效。Java 打印机属性仍然不起作用,如果您想设置它们,您将需要找到另一种方法。
回答by ZachOfAllTrades
The problem is that the the Java print API is a bridge between worlds. Printer manufacturers don't release drivers for the JVM. They release drivers for Windows, Macintosh, and maybe someone has a a driver for a given printer that works on one or more *nix platforms.
问题在于 Java 打印 API 是世界之间的桥梁。打印机制造商不发布 JVM 的驱动程序。他们发布了适用于 Windows、Macintosh 的驱动程序,也许有人拥有适用于一个或多个 *nix 平台的给定打印机的驱动程序。
Along you come with some Java code running inside a JVM on some host system. When you start querying the printer features, you aren't talking to the printers -- you are talking to a bridge class in java.awt.print that hook into the JVM, which hooks to the host operating system, which hooks into whatever particular driver was installed for a given printer. So there are several places where this can fall apart... The particular JVM you are on may or may not fully implement the API for querying printer features, let alone passing those parameters along for a given job.
随之而来的是一些在某个主机系统上的 JVM 中运行的 Java 代码。当您开始查询打印机功能时,您并不是在与打印机对话——您是在与 java.awt.print 中的桥接类对话,该桥类连接到 JVM,它连接到主机操作系统,它连接到任何特定的为给定的打印机安装了驱动程序。所以有几个地方可能会崩溃……您所在的特定 JVM 可能会也可能不会完全实现用于查询打印机功能的 API,更不用说为给定的作业传递这些参数了。
A few suggestions:
几点建议:
- look into the javax.print classes as an alternative to java.awt.print -- I've had more luck printing from there.
- try using alternative print drivers for your printers -- you can define multiple named connections to a given printer, each with a different driver. If you've got a manufacturer provided driver, try a more generic driver, if you've got a generic driver, try to install a more specific one.
- run your code under alternate JVM implementations for your platform
- 查看 javax.print 类作为 java.awt.print 的替代品——从那里我有更多的运气打印。
- 尝试为您的打印机使用替代打印驱动程序——您可以为给定的打印机定义多个命名连接,每个连接都有不同的驱动程序。如果您有制造商提供的驱动程序,请尝试更通用的驱动程序,如果您有通用驱动程序,请尝试安装更具体的驱动程序。
- 在您平台的替代 JVM 实现下运行您的代码
回答by Reddymails
We had similar requirement to print PDF's and wanted to send some pages to Specific tray and also wanted the document to be stapled. We used Java code + ghost script combination First convert PDF to ghost script and then add PJL (Print job language) commands to ghost script file to select trays and staple the documents. Then send that edited ghost script file to printer.
我们对打印 PDF 有类似的要求,并希望将一些页面发送到特定托盘,还希望将文档装订。我们使用Java代码+ghost脚本组合先将PDF转换为ghost脚本,然后在ghost脚本文件中添加PJL(打印作业语言)命令来选择托盘并装订文档。然后将编辑过的 Ghost 脚本文件发送到打印机。
Here is complete example written in Java
这是用 Java 编写的完整示例
http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html
http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html
-Ram
-内存
回答by Matt
Here's what it looks like in javafx Tray's may vary and it will also print out all trays that are available just change the tray name
这是它在 javafx 中的样子 Tray's 可能会有所不同,它还会打印出所有可用的托盘,只需更改托盘名称
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();
}
}
}
Here's My output:
这是我的输出:
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
回答by tresf
I've found the trick for the printer trays is to iterate over the Media.class
using getSupportedAttributeValues(...)
, match the human-readable name, and select that particular value. Tested on Windows, MacOS with several tray configurations.
我发现打印机托盘的技巧是迭代Media.class
using 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);