我已经配置了一个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"
我还有第二台虚拟打印机,可以很好地工作,并且它们之间似乎没有太大的区别:相同的驱动程序,相同的端口参数,相同的设置,非常相似的代码。但是,经过一定数量的页面后它不会冻结,并且输出文件符合预期。
是什么导致我的打印机停止响应?
答案 0 :(得分:0)
事实证明您的打印机没有问题,但是您的代码没有问题。更具体地说,您如何[不]处理运行时流。您缺少的过程是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);
}
}
}