我已经配置了一台PDF打印机,它使用Ghostscript将文档转换为PDF,然后由我的Java桌面应用程序处理和使用。它通过RedMon端口重定向打印机数据。对于我打印的大多数文档,它工作正常并按预期生成PDF文件。但是,对于具有一定页数的文档,该过程只是冻结:不会抛出任何错误,该过程只是成立。它似乎独立于文件大小或打印机属性(虽然后者似乎影响了打印的页面数)。
在停止Java应用程序之后,我留下了一个具有固定页数的文档(通常是265页,但它也恰好以263页或247页结束)。倒数第二页不完整(如部分打印的表格和文本),而最后一页打印为错误:
ERROR: syntaxerror
OFFENDING COMMAND: --nostringval--
STACK:
/[NUMBER]
其中[NUMBER]是任何给定的单位数字。
这是我的Ghostscript集成商类:
public class GhostScriptIntegrator {
public static void createPDF(String[] args, String filename) {
if (args.length > 0) {
try {
Process process = Runtime.getRuntime().exec(
args[0] + " -sOutputFile=\"" + filename
+ "\" -c save pop -f -");
OutputStream os = process.getOutputStream();
BufferedReader sc = null;
try (PrintWriter writer = new PrintWriter(os)) {
sc = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = sc.readLine()) != null) {
writer.println(line);
}
writer.flush();
} catch (Exception ex) {
Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (sc != null) {
sc.close();
}
}
process.waitFor();
} catch (InterruptedException | IOException ex) {
Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
args
参数由我的虚拟打印机处理(类似于我在previous post中呈现的方式):
完整论点:
-jar "C:\Program Files (x86)\Impressora SPE\ImpressoraSPE.jar" "C:\Program Files (x86)\gs\gs9.21\bin\gswin32c -I\"C:\Program Files (x86)\gs\gs9.21\lib\" -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sPAPERSIZE=a4 -q -dPDFA=2 -dPDFACompatibilityPolicy=1 -dSimulateOverprint=true -dCompatibilityLevel=1.3 -dPDFSETTINGS=/screen -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=150"
我有一个完美的第二个虚拟打印机,它们之间似乎没有显着差异:相同的驱动程序,相同的端口参数,相同的设置,非常相似的代码。然而,它在一定数量的页面之后不会冻结,并且输出文件是预期的。
是什么原因导致我的打印机停止响应?
事实证明您的打印机没有问题,而是您的代码。更具体地说,您[不]处理运行时流的方式。你的过程缺少的是StreamGobbler。
StreamGobbler是一个InputStream,它使用内部工作线程来持续消耗来自另一个InputStream的输入。它使用缓冲区来存储消耗的数据。如果需要,将自动调整缓冲区大小。
您的进程挂起,因为它无法完全读取输入流。以下文章提供了一个非常深入的解释,说明为什么会发生以及如何解决它:
When Runtime.exec() won't - Part 1 When Runtime.exec() won't - Part 2
但引用文章本身(反过来引用JDK Javadoc):
由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞甚至死锁。
解决方案是通过实现StreamGobbler类简单地耗尽进程中的每个输入流:
public class GhostScriptIntegrator {
public static void createPDF(String[] args, String filename) throws FileNotFoundException {
if (args.length > 0) {
try {
Process process = Runtime.getRuntime().exec(
args[0] + " -sOutputFile=\"" + filename
+ "\" -c save pop -f -");
OutputStream os = process.getOutputStream();
BufferedReader sc = null;
InputStreamReader ir = new InputStreamReader(System.in);
try (PrintWriter writer = new PrintWriter(os)) {
StreamGobbler errorGobbler = new StreamGobbler(
process.getErrorStream(), "ERROR");
StreamGobbler outputGobbler = new StreamGobbler(
process.getInputStream(), "OUTPUT");
errorGobbler.start();
outputGobbler.start();
sc = new BufferedReader(ir);
String line;
while ((line = sc.readLine()) != null) {
writer.println(line);
writer.flush();
}
} catch (IOException ex) {
Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (sc != null) {
sc.close();
}
ir.close();
if (os != null) {
os.close();
}
}
process.waitFor();
} catch (InterruptedException | IOException ex) {
Logger.getLogger(GhostScriptIntegrator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
class StreamGobbler extends Thread {
InputStream is;
String type;
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
long contador = 0;
while (br.readLine() != null) {
//Do nothing
}
} catch (IOException ex) {
Logger.getLogger(StreamGobbler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}